From 4d514d5a606f43b5fbbb163a7244f5e19d0f6e88 Mon Sep 17 00:00:00 2001 From: "theraysmith@gmail.com" Date: Mon, 23 Sep 2013 15:26:50 +0000 Subject: [PATCH] Major refactor of beam search, elimination of dead code, misc bug fixes, updates to Makefile.am, Changelog etc. git-svn-id: https://tesseract-ocr.googlecode.com/svn/trunk@878 d0cd1f9f-072b-0410-8dd7-cf729c803f20 --- ChangeLog | 12 + api/Makefile.am | 4 +- api/capi.cpp | 14 +- api/capi.h | 6 +- ccmain/Makefile.am | 4 +- ccmain/adaptions.cpp | 17 +- ccmain/applybox.cpp | 102 +- ccmain/control.cpp | 329 +- ccmain/cube_control.cpp | 106 +- ccmain/docqual.cpp | 17 +- ccmain/equationdetect.cpp | 18 +- ccmain/fixspace.cpp | 100 +- ccmain/fixxht.cpp | 12 +- ccmain/imgscale.cpp | 2 +- ccmain/ltrresultiterator.cpp | 99 +- ccmain/ltrresultiterator.h | 15 +- ccmain/output.cpp | 10 +- ccmain/pageiterator.cpp | 38 +- ccmain/pageiterator.h | 9 + ccmain/paragraphs.cpp | 101 +- ccmain/recogtraining.cpp | 81 +- ccmain/reject.cpp | 296 +- ccmain/reject.h | 4 +- ccmain/resultiterator.h | 3 +- ccmain/scaleimg.cpp | 1 + ccmain/tessbox.cpp | 62 +- ccmain/tessedit.cpp | 63 +- ccmain/tesseract_cube_combiner.cpp | 12 +- ccmain/tfacep.h | 37 - ccmain/tfacepp.cpp | 400 +- ccmain/tfacepp.h | 41 - ccmain/werdit.cpp | 2 +- ccmain/werdit.h | 2 +- ccstruct/Makefile.am | 6 +- ccstruct/blamer.cpp | 587 + ccstruct/blamer.h | 330 + ccstruct/boxword.cpp | 70 +- ccstruct/boxword.h | 31 +- ccstruct/matrix.cpp | 107 +- ccstruct/matrix.h | 214 +- ccstruct/ocrblock.cpp | 2 + ccstruct/pageres.cpp | 803 +- ccstruct/pageres.h | 351 +- ccstruct/params_training_featdef.cpp | 40 + ccstruct/params_training_featdef.h | 133 +- ccstruct/ratngs.cpp | 665 +- ccstruct/ratngs.h | 333 +- ccstruct/rect.cpp | 10 + ccstruct/rect.h | 12 +- ccstruct/seam.cpp | 319 +- ccstruct/seam.h | 109 +- ccstruct/split.cpp | 112 +- ccstruct/vecfuncs.h | 3 +- ccstruct/werd.cpp | 2 +- ccutil/Makefile.am | 14 +- ccutil/ambigs.cpp | 252 +- ccutil/ambigs.h | 34 +- ccutil/doubleptr.h | 93 + ccutil/errcode.h | 6 - ccutil/genericheap.h | 225 + ccutil/genericvector.h | 135 +- ccutil/globaloc.cpp | 102 +- ccutil/globaloc.h | 14 +- ccutil/kdpair.h | 189 + ccutil/object_cache.h | 125 + ccutil/params.cpp | 21 + ccutil/params.h | 24 + ccutil/platform.h | 16 +- ccutil/sorthelper.h | 4 +- ccutil/strngs.cpp | 21 +- ccutil/strngs.h | 5 +- ccutil/tesscallback.h | 9357 +++++++++- ccutil/tessdatamanager.cpp | 3 +- ccutil/tessdatamanager.h | 20 +- ccutil/tprintf.cpp | 80 +- ccutil/tprintf.h | 42 +- ccutil/universalambigs.cpp | 21370 +++++++++++++++++++++++ ccutil/universalambigs.h | 26 + configure.ac | 10 +- cube/char_set.cpp | 6 +- cube/conv_net_classifier.cpp | 6 +- cube/cube_line_segmenter.cpp | 8 +- cube/cube_object.cpp | 2 +- cube/hybrid_neural_net_classifier.cpp | 8 +- cube/tess_lang_mod_edge.cpp | 2 +- cube/word_list_lang_model.cpp | 4 +- cutil/Makefile.am | 6 +- cutil/bitvec.cpp | 21 - cutil/bitvec.h | 2 - cutil/danerror.cpp | 2 +- cutil/listio.cpp | 1 - cutil/oldheap.cpp | 334 - cutil/oldheap.h | 80 - cutil/tessarray.cpp | 115 - cutil/tessarray.h | 166 - dict/Makefile.am | 8 +- dict/dawg.cpp | 58 +- dict/dawg.h | 108 +- dict/dawg_cache.cpp | 102 + dict/dawg_cache.h | 56 + dict/dict.cpp | 710 +- dict/dict.h | 607 +- dict/hyphen.cpp | 9 +- dict/permdawg.cpp | 511 +- dict/permute.cpp | 1565 -- dict/permute.h | 150 - dict/states.cpp | 274 - dict/states.h | 74 - dict/stopper.cpp | 872 +- dict/stopper.h | 48 +- dict/trie.cpp | 157 +- dict/trie.h | 39 +- textord/Makefile.am | 5 +- textord/bbgrid.h | 49 +- textord/colpartition.cpp | 11 +- textord/colpartitionset.cpp | 12 +- textord/colpartitionset.h | 2 +- textord/drawtord.cpp | 4 +- textord/drawtord.h | 7 +- textord/edgblob.cpp | 10 +- textord/fpchop.cpp | 384 +- textord/fpchop.h | 36 - textord/gap_map.cpp | 10 +- textord/linefind.cpp | 3 +- textord/makerow.cpp | 184 +- textord/makerow.h | 6 +- textord/textord.cpp | 28 +- textord/textord.h | 30 +- textord/tordmain.cpp | 92 +- textord/tospace.cpp | 5 +- textord/underlin.cpp | 7 - textord/workingpartset.cpp | 7 +- training/Makefile.am | 37 +- training/ambiguous_words.cpp | 2 +- training/classifier_tester.cpp | 134 +- training/combine_tessdata.cpp | 8 +- training/commandlineflags.cpp | 319 + training/commandlineflags.h | 83 + training/commontraining.cpp | 175 +- training/commontraining.h | 27 +- training/fileio.cpp | 137 + training/fileio.h | 93 + training/icuerrorcode.h | 66 + training/normstrngs.cpp | 150 + training/normstrngs.h | 56 + training/set_unicharset_properties.cpp | 196 + training/shapeclustering.cpp | 2 +- training/unicharset_extractor.cpp | 3 +- training/wordlist2dawg.cpp | 85 +- viewer/svutil.cpp | 13 +- viewer/svutil.h | 2 +- wordrec/Makefile.am | 24 +- wordrec/associate.cpp | 94 +- wordrec/associate.h | 73 +- wordrec/bestfirst.cpp | 772 - wordrec/bestfirst.h | 70 - wordrec/chop.cpp | 28 +- wordrec/chop.h | 8 +- wordrec/chopper.cpp | 852 +- wordrec/chopper.h | 3 +- wordrec/closed.cpp | 130 - wordrec/closed.h | 43 - wordrec/findseam.cpp | 248 +- wordrec/findseam.h | 8 +- wordrec/gradechop.cpp | 9 +- wordrec/heuristic.cpp | 342 - wordrec/language_model.cpp | 2143 +-- wordrec/language_model.h | 660 +- wordrec/lm_consistency.cpp | 113 + wordrec/lm_consistency.h | 143 + wordrec/lm_pain_points.cpp | 216 + wordrec/lm_pain_points.h | 138 + wordrec/lm_state.cpp | 81 + wordrec/lm_state.h | 239 + wordrec/matchtab.cpp | 214 - wordrec/matchtab.h | 64 - wordrec/params_model.cpp | 174 + wordrec/params_model.h | 89 + wordrec/pieces.cpp | 154 +- wordrec/plotseg.cpp | 106 - wordrec/plotseg.h | 48 - wordrec/segsearch.cpp | 517 +- wordrec/tface.cpp | 32 +- wordrec/wordclass.cpp | 105 +- wordrec/wordclass.h | 38 - wordrec/wordrec.cpp | 102 +- wordrec/wordrec.h | 408 +- 187 files changed, 41117 insertions(+), 14408 deletions(-) delete mode 100644 ccmain/tfacep.h delete mode 100644 ccmain/tfacepp.h create mode 100644 ccstruct/blamer.cpp create mode 100644 ccstruct/blamer.h create mode 100644 ccstruct/params_training_featdef.cpp create mode 100644 ccutil/doubleptr.h create mode 100644 ccutil/genericheap.h create mode 100644 ccutil/kdpair.h create mode 100644 ccutil/object_cache.h create mode 100644 ccutil/universalambigs.cpp create mode 100644 ccutil/universalambigs.h delete mode 100644 cutil/oldheap.cpp delete mode 100644 cutil/oldheap.h delete mode 100644 cutil/tessarray.cpp delete mode 100644 cutil/tessarray.h create mode 100644 dict/dawg_cache.cpp create mode 100644 dict/dawg_cache.h delete mode 100644 dict/permute.cpp delete mode 100644 dict/permute.h delete mode 100644 dict/states.cpp delete mode 100644 dict/states.h create mode 100644 training/commandlineflags.cpp create mode 100644 training/commandlineflags.h create mode 100644 training/fileio.cpp create mode 100644 training/fileio.h create mode 100644 training/icuerrorcode.h create mode 100644 training/normstrngs.cpp create mode 100644 training/normstrngs.h create mode 100644 training/set_unicharset_properties.cpp delete mode 100644 wordrec/bestfirst.cpp delete mode 100644 wordrec/bestfirst.h delete mode 100644 wordrec/closed.cpp delete mode 100644 wordrec/closed.h delete mode 100644 wordrec/heuristic.cpp create mode 100644 wordrec/lm_consistency.cpp create mode 100644 wordrec/lm_consistency.h create mode 100644 wordrec/lm_pain_points.cpp create mode 100644 wordrec/lm_pain_points.h create mode 100644 wordrec/lm_state.cpp create mode 100644 wordrec/lm_state.h delete mode 100644 wordrec/matchtab.cpp delete mode 100644 wordrec/matchtab.h create mode 100644 wordrec/params_model.cpp create mode 100644 wordrec/params_model.h delete mode 100644 wordrec/plotseg.cpp delete mode 100644 wordrec/plotseg.h delete mode 100644 wordrec/wordclass.h diff --git a/ChangeLog b/ChangeLog index 328fad7982..a5d86224a8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2013-09-20 v3.03 +* Added Renderer to API to allow document-level processing and output + of document formats, like hOCR, PDF. +* Major refactor of word-level recognition, beam search, eliminating dead code. +* Refactored classifier to make it easier to add new ones. +* Generalized feature extractor to allow feature extraction from greyscale. +* Improved sub/superscript treatment. +* Improved baseline fit. +* Added set_unicharset_properties to training tools. +* Many bug fixes. + + 2012-02-01 - v3.02 * Moved ResultIterator/PageIterator to ccmain. * Added Right-to-left/Bidi capability in the output iterators for Hebrew/Arabic. diff --git a/api/Makefile.am b/api/Makefile.am index 8c236e30a1..88fff38263 100644 --- a/api/Makefile.am +++ b/api/Makefile.am @@ -9,7 +9,7 @@ if VISIBILITY AM_CPPFLAGS += -fvisibility=hidden -fvisibility-inlines-hidden endif -include_HEADERS = apitypes.h baseapi.h capi.h +include_HEADERS = apitypes.h baseapi.h capi.h renderer.h lib_LTLIBRARIES = if !USING_MULTIPLELIBS @@ -35,7 +35,7 @@ libtesseract_api_la_CPPFLAGS = $(AM_CPPFLAGS) if VISIBILITY libtesseract_api_la_CPPFLAGS += -DTESS_EXPORTS endif -libtesseract_api_la_SOURCES = baseapi.cpp capi.cpp +libtesseract_api_la_SOURCES = baseapi.cpp capi.cpp renderer.cpp lib_LTLIBRARIES += libtesseract.la libtesseract_la_LDFLAGS = diff --git a/api/capi.cpp b/api/capi.cpp index 55362a9a3e..4d9e0554a4 100644 --- a/api/capi.cpp +++ b/api/capi.cpp @@ -2,6 +2,8 @@ # define TESS_CAPI_INCLUDE_BASEAPI #endif #include "capi.h" +#include "genericvector.h" +#include "strngs.h" TESS_API const char* TESS_CALL TessVersion() { @@ -382,10 +384,10 @@ TESS_API BOOL TESS_CALL TessBaseAPIDetectOS(TessBaseAPI* handle, OSResults* resu return handle->DetectOS(results) ? TRUE : FALSE; } -TESS_API void TESS_CALL TessBaseAPIGetFeaturesForBlob(TessBaseAPI* handle, TBLOB* blob, const DENORM* denorm, INT_FEATURE_ARRAY int_features, +TESS_API void TESS_CALL TessBaseAPIGetFeaturesForBlob(TessBaseAPI* handle, TBLOB* blob, INT_FEATURE_STRUCT* int_features, int* num_features, int* FeatureOutlineIndex) { - handle->GetFeaturesForBlob(blob, *denorm, int_features, num_features, FeatureOutlineIndex); + handle->GetFeaturesForBlob(blob, int_features, num_features, FeatureOutlineIndex); } TESS_API ROW* TESS_CALL TessFindRowForBox(BLOCK_LIST* blocks, int left, int top, int right, int bottom) @@ -393,10 +395,10 @@ TESS_API ROW* TESS_CALL TessFindRowForBox(BLOCK_LIST* blocks, int left, int top, return TessBaseAPI::FindRowForBox(blocks, left, top, right, bottom); } -TESS_API void TESS_CALL TessBaseAPIRunAdaptiveClassifier(TessBaseAPI* handle, TBLOB* blob, const DENORM* denorm, int num_max_matches, +TESS_API void TESS_CALL TessBaseAPIRunAdaptiveClassifier(TessBaseAPI* handle, TBLOB* blob, int num_max_matches, int* unichar_ids, float* ratings, int* num_matches_returned) { - handle->RunAdaptiveClassifier(blob, *denorm, num_max_matches, unichar_ids, ratings, num_matches_returned); + handle->RunAdaptiveClassifier(blob, num_max_matches, unichar_ids, ratings, num_matches_returned); } TESS_API const char* TESS_CALL TessBaseAPIGetUnichar(TessBaseAPI* handle, int unichar_id) @@ -424,9 +426,9 @@ TESS_API TBLOB* TESS_CALL TessMakeTBLOB(struct Pix *pix) return TessBaseAPI::MakeTBLOB(pix); } -TESS_API void TESS_CALL TessNormalizeTBLOB(TBLOB *tblob, ROW *row, BOOL numeric_mode, DENORM *denorm) +TESS_API void TESS_CALL TessNormalizeTBLOB(TBLOB *tblob, ROW *row, BOOL numeric_mode) { - TessBaseAPI::NormalizeTBLOB(tblob, row, numeric_mode != FALSE, denorm); + TessBaseAPI::NormalizeTBLOB(tblob, row, numeric_mode != FALSE); } TESS_API TessOcrEngineMode TESS_CALL TessBaseAPIOem(const TessBaseAPI* handle) diff --git a/api/capi.h b/api/capi.h index bc39b362a6..8f4bfdd9d4 100644 --- a/api/capi.h +++ b/api/capi.h @@ -205,11 +205,11 @@ TESS_API void TESS_CALL TessBaseAPISetProbabilityInContextFunc(TessBaseAPI* han TESS_API void TESS_CALL TessBaseAPISetFillLatticeFunc(TessBaseAPI* handle, TessFillLatticeFunc f); TESS_API BOOL TESS_CALL TessBaseAPIDetectOS(TessBaseAPI* handle, OSResults* results); -TESS_API void TESS_CALL TessBaseAPIGetFeaturesForBlob(TessBaseAPI* handle, TBLOB* blob, const DENORM* denorm, INT_FEATURE_ARRAY int_features, +TESS_API void TESS_CALL TessBaseAPIGetFeaturesForBlob(TessBaseAPI* handle, TBLOB* blob, INT_FEATURE_STRUCT* int_features, int* num_features, int* FeatureOutlineIndex); TESS_API ROW* TESS_CALL TessFindRowForBox(BLOCK_LIST* blocks, int left, int top, int right, int bottom); -TESS_API void TESS_CALL TessBaseAPIRunAdaptiveClassifier(TessBaseAPI* handle, TBLOB* blob, const DENORM* denorm, int num_max_matches, +TESS_API void TESS_CALL TessBaseAPIRunAdaptiveClassifier(TessBaseAPI* handle, TBLOB* blob, int num_max_matches, int* unichar_ids, float* ratings, int* num_matches_returned); #endif @@ -226,7 +226,7 @@ TESS_API int TESS_CALL TessBaseAPINumDawgs(const TessBaseAPI* handle); TESS_API ROW* TESS_CALL TessMakeTessOCRRow(float baseline, float xheight, float descender, float ascender); TESS_API TBLOB* TESS_CALL TessMakeTBLOB(Pix *pix); -TESS_API void TESS_CALL TessNormalizeTBLOB(TBLOB *tblob, ROW *row, BOOL numeric_mode, DENORM *denorm); +TESS_API void TESS_CALL TessNormalizeTBLOB(TBLOB *tblob, ROW *row, BOOL numeric_mode); TESS_API TessOcrEngineMode TESS_CALL TessBaseAPIOem(const TessBaseAPI* handle); diff --git a/ccmain/Makefile.am b/ccmain/Makefile.am index f6c6f05ef7..20f8de18a3 100644 --- a/ccmain/Makefile.am +++ b/ccmain/Makefile.am @@ -19,7 +19,7 @@ noinst_HEADERS = \ equationdetect.h fixspace.h imgscale.h mutableiterator.h osdetect.h \ output.h paragraphs.h paragraphs_internal.h paramsd.h pgedit.h \ reject.h scaleimg.h tessbox.h tessedit.h tesseractclass.h \ - tesseract_cube_combiner.h tessvars.h tfacep.h tfacepp.h werdit.h + tesseract_cube_combiner.h tessvars.h werdit.h if !USING_MULTIPLELIBS noinst_LTLIBRARIES = libtesseract_main.la @@ -46,7 +46,7 @@ libtesseract_main_la_SOURCES = \ imgscale.cpp ltrresultiterator.cpp \ osdetect.cpp output.cpp pageiterator.cpp pagesegmain.cpp \ pagewalk.cpp paragraphs.cpp paramsd.cpp pgedit.cpp recogtraining.cpp \ - reject.cpp resultiterator.cpp scaleimg.cpp \ + reject.cpp resultiterator.cpp scaleimg.cpp superscript.cpp \ tesseract_cube_combiner.cpp \ tessbox.cpp tessedit.cpp tesseractclass.cpp tessvars.cpp \ tfacepp.cpp thresholder.cpp \ diff --git a/ccmain/adaptions.cpp b/ccmain/adaptions.cpp index 0e958a7782..d8c2f11333 100644 --- a/ccmain/adaptions.cpp +++ b/ccmain/adaptions.cpp @@ -114,27 +114,12 @@ BOOL8 Tesseract::word_adaptable( //should we adapt? return FALSE; } -// if (flags.bit (CHECK_AMBIG_WERD) && test_ambig_word (word)) if (flags.bit (CHECK_AMBIG_WERD) && - !getDict().NoDangerousAmbig(word->best_choice, NULL, false, NULL, NULL)) { + word->best_choice->dangerous_ambig_found()) { if (tessedit_adaption_debug) tprintf("word is ambiguous\n"); return FALSE; } - // Do not adapt to words that are composed from fragments if - // tessedit_adapt_to_char_fragments is false. - if (!tessedit_adapt_to_char_fragments) { - const char *fragment_lengths = word->best_choice->fragment_lengths(); - if (fragment_lengths != NULL && *fragment_lengths != '\0') { - for (int i = 0; i < word->best_choice->length(); ++i) { - if (fragment_lengths[i] > 1) { - if (tessedit_adaption_debug) tprintf("won't adapt to fragments\n"); - return false; // found a character composed from fragments - } - } - } - } - if (tessedit_adaption_debug) { tprintf("returning status %d\n", status); } diff --git a/ccmain/applybox.cpp b/ccmain/applybox.cpp index e94e602f57..3dc597ce2f 100644 --- a/ccmain/applybox.cpp +++ b/ccmain/applybox.cpp @@ -235,21 +235,6 @@ PAGE_RES* Tesseract::SetupApplyBoxes(const GenericVector& boxes, return page_res; } -// Helper to make a WERD_CHOICE from the BLOB_CHOICE_LIST_VECTOR using only -// the top choices. Avoids problems with very long words. -static void MakeWordChoice(const BLOB_CHOICE_LIST_VECTOR& char_choices, - const UNICHARSET& unicharset, - WERD_CHOICE* word_choice) { - *word_choice = WERD_CHOICE(&unicharset); // clear the word choice. - word_choice->make_bad(); - for (int i = 0; i < char_choices.size(); ++i) { - BLOB_CHOICE_IT it(char_choices[i]); - BLOB_CHOICE* bc = it.data(); - word_choice->append_unichar_id(bc->unichar_id(), 1, - bc->rating(), bc->certainty()); - } -} - // Tests the chopper by exhaustively running chop_one_blob. // The word_res will contain filled chopped_word, seam_array, denorm, // box_word and best_state for the maximally chopped word. @@ -257,7 +242,8 @@ void Tesseract::MaximallyChopWord(const GenericVector& boxes, BLOCK* block, ROW* row, WERD_RES* word_res) { if (!word_res->SetupForTessRecognition(unicharset, this, BestPix(), false, - this->textord_use_cjk_fp_model, + textord_use_cjk_fp_model, + poly_allow_detailed_fx, row, block)) { word_res->CloneChoppedToRebuild(); return; @@ -266,13 +252,10 @@ void Tesseract::MaximallyChopWord(const GenericVector& boxes, tprintf("Maximally chopping word at:"); word_res->word->bounding_box().print(); } - blob_match_table.init_match_table(); - BLOB_CHOICE_LIST *match_result; - BLOB_CHOICE_LIST_VECTOR *char_choices = new BLOB_CHOICE_LIST_VECTOR(); - ASSERT_HOST(word_res->chopped_word->blobs != NULL); + GenericVector blob_choices; + ASSERT_HOST(!word_res->chopped_word->blobs.empty()); float rating = static_cast(MAX_INT8); - for (TBLOB* blob = word_res->chopped_word->blobs; blob != NULL; - blob = blob->next) { + for (int i = 0; i < word_res->chopped_word->NumBlobs(); ++i) { // The rating and certainty are not quite arbitrary. Since // select_blob_to_chop uses the worst certainty to choose, they all have // to be different, so starting with MAX_INT8, subtract 1/8 for each blob @@ -281,32 +264,33 @@ void Tesseract::MaximallyChopWord(const GenericVector& boxes, // produced, however much chopping is required. The chops are thus only // limited by the ability of the chopper to find suitable chop points, // and not by the value of the certainties. - match_result = fake_classify_blob(0, rating, -rating); - modify_blob_choice(match_result, 0); - ASSERT_HOST(!match_result->empty()); - *char_choices += match_result; + BLOB_CHOICE* choice = + new BLOB_CHOICE(0, rating, -rating, -1, -1, 0, 0, 0, 0, BCC_FAKE); + blob_choices.push_back(choice); rating -= 0.125f; } - inT32 blob_number; + const double e = exp(1.0); // The base of natural logs. + int blob_number; int right_chop_index = 0; if (!assume_fixed_pitch_char_segment) { // We only chop if the language is not fixed pitch like CJK. - if (prioritize_division) { - while (chop_one_blob2(boxes, word_res, &word_res->seam_array)); - } else { - while (chop_one_blob(word_res->chopped_word, char_choices, - &blob_number, &word_res->seam_array, - &right_chop_index)); + SEAM* seam = NULL; + while ((seam = chop_one_blob(boxes, blob_choices, word_res, + &blob_number)) != NULL) { + word_res->InsertSeam(blob_number, seam); + BLOB_CHOICE* left_choice = blob_choices[blob_number]; + rating = left_choice->rating() / e; + left_choice->set_rating(rating); + left_choice->set_certainty(-rating); + // combine confidence w/ serial # + BLOB_CHOICE* right_choice = new BLOB_CHOICE(++right_chop_index, + rating - 0.125f, -rating, + -1, -1, 0, 0, 0, 0, BCC_FAKE); + blob_choices.insert(right_choice, blob_number + 1); } } - MakeWordChoice(*char_choices, unicharset, word_res->best_choice); - MakeWordChoice(*char_choices, unicharset, word_res->raw_choice); word_res->CloneChoppedToRebuild(); - blob_match_table.end_match_table(); - if (char_choices != NULL) { - char_choices->delete_data_pointers(); - delete char_choices; - } + word_res->FakeClassifyWord(blob_choices.size(), &blob_choices[0]); } // Helper to compute the dispute resolution metric. @@ -558,7 +542,6 @@ bool Tesseract::ConvertStringToUnichars(const char* utf8, // substitutions ARE used. bool Tesseract::FindSegmentation(const GenericVector& target_text, WERD_RES* word_res) { - blob_match_table.init_match_table(); // Classify all required combinations of blobs and save results in choices. int word_length = word_res->box_word->length(); GenericVector* choices = @@ -566,8 +549,8 @@ bool Tesseract::FindSegmentation(const GenericVector& target_text, for (int i = 0; i < word_length; ++i) { for (int j = 1; j <= kMaxGroupSize && i + j <= word_length; ++j) { BLOB_CHOICE_LIST* match_result = classify_piece( - word_res->chopped_word->blobs, word_res->denorm, word_res->seam_array, - i, i + j - 1, word_res->blamer_bundle); + word_res->seam_array, i, i + j - 1, "Applybox", + word_res->chopped_word, word_res->blamer_bundle); if (applybox_debug > 2) { tprintf("%d+%d:", i, j); print_ratings_list("Segment:", match_result, unicharset); @@ -583,7 +566,6 @@ bool Tesseract::FindSegmentation(const GenericVector& target_text, float best_rating = 0.0f; SearchForText(choices, 0, word_length, target_text, 0, 0.0f, &search_segmentation, &best_rating, &word_res->best_state); - blob_match_table.end_match_table(); for (int i = 0; i < word_length; ++i) choices[i].delete_data_pointers(); delete [] choices; @@ -591,9 +573,8 @@ bool Tesseract::FindSegmentation(const GenericVector& target_text, // Build the original segmentation and if it is the same length as the // truth, assume it will do. int blob_count = 1; - for (int s = 0; s < array_count(word_res->seam_array); ++s) { - SEAM* seam = - reinterpret_cast(array_value(word_res->seam_array, s)); + for (int s = 0; s < word_res->seam_array.size(); ++s) { + SEAM* seam = word_res->seam_array[s]; if (seam->split1 == NULL) { word_res->best_state.push_back(blob_count); blob_count = 1; @@ -707,21 +688,25 @@ void Tesseract::TidyUp(PAGE_RES* page_res) { WERD_RES* word_res; for (; (word_res = pr_it.word()) != NULL; pr_it.forward()) { int ok_in_word = 0; - BLOB_CHOICE_LIST_VECTOR char_choices; - for (int i = word_res->correct_text.size() - 1; i >= 0; i--) { - if (word_res->correct_text[i].length() > 0) { + int blob_count = word_res->correct_text.size(); + WERD_CHOICE* word_choice = new WERD_CHOICE(word_res->uch_set, blob_count); + word_choice->set_permuter(TOP_CHOICE_PERM); + for (int c = 0; c < blob_count; ++c) { + if (word_res->correct_text[c].length() > 0) { ++ok_in_word; } // Since we only need a fake word_res->best_choice, the actual // unichar_ids do not matter. Which is fortunate, since TidyUp() // can be called while training Tesseract, at the stage where // unicharset is not meaningful yet. - char_choices += fake_classify_blob(INVALID_UNICHAR_ID, 1.0, -1.0); + word_choice->append_unichar_id_space_allocated( + INVALID_UNICHAR_ID, word_res->best_state[c], 1.0f, -1.0f); } if (ok_in_word > 0) { ok_blob_count += ok_in_word; bad_blob_count += word_res->correct_text.size() - ok_in_word; - MakeWordChoice(char_choices, unicharset, word_res->best_choice); + word_res->LogNewRawChoice(word_choice); + word_res->LogNewCookedChoice(1, false, word_choice); } else { ++unlabelled_words; if (applybox_debug > 0) { @@ -730,7 +715,6 @@ void Tesseract::TidyUp(PAGE_RES* page_res) { } pr_it.DeleteCurrentWord(); } - char_choices.delete_data_pointers(); } pr_it.restart_page(); for (; (word_res = pr_it.word()) != NULL; pr_it.forward()) { @@ -772,11 +756,13 @@ void Tesseract::CorrectClassifyWords(PAGE_RES* page_res) { GenericVector tokens; word_res->correct_text[i].split(' ', &tokens); UNICHAR_ID char_id = unicharset.unichar_to_id(tokens[0].string()); - choice->append_unichar_id_space_allocated(char_id, 1, 0.0f, 0.0f); + choice->append_unichar_id_space_allocated(char_id, + word_res->best_state[i], + 0.0f, 0.0f); } - if (word_res->best_choice != NULL) - delete word_res->best_choice; - word_res->best_choice = choice; + word_res->ClearWordChoices(); + word_res->LogNewRawChoice(choice); + word_res->LogNewCookedChoice(1, false, choice); } } @@ -787,7 +773,7 @@ void Tesseract::ApplyBoxTraining(const STRING& filename, PAGE_RES* page_res) { int word_count = 0; for (WERD_RES *word_res = pr_it.word(); word_res != NULL; word_res = pr_it.forward()) { - LearnWord(filename.string(), NULL, word_res); + LearnWord(filename.string(), word_res); ++word_count; } tprintf("Generated training data for %d words\n", word_count); diff --git a/ccmain/control.cpp b/ccmain/control.cpp index 2b27529d38..0110f2970b 100644 --- a/ccmain/control.cpp +++ b/ccmain/control.cpp @@ -29,7 +29,6 @@ #include "ocrclass.h" #include "werdit.h" #include "drawfx.h" -#include "tfacep.h" #include "tessbox.h" #include "tessvars.h" #include "pgedit.h" @@ -55,6 +54,9 @@ const char* const kBackUpConfigFile = "tempconfigdata.config"; // Multiple of x-height to make a repeated word have spaces in it. const double kRepcharGapThreshold = 0.5; +// Min believable x-height for any text when refitting as a fraction of +// original x-height +const double kMinRefitXHeightFraction = 0.5; /** @@ -293,9 +295,9 @@ bool Tesseract::recog_all_words(PAGE_RES* page_res, // Update misadaption log (we only need to do it on pass 1, since // adaption only happens on this pass). if (page_res_it.word()->blamer_bundle != NULL && - page_res_it.word()->blamer_bundle->misadaption_debug.length() > 0) { + page_res_it.word()->blamer_bundle->misadaption_debug().length() > 0) { page_res->misadaption_log.push_back( - page_res_it.word()->blamer_bundle->misadaption_debug); + page_res_it.word()->blamer_bundle->misadaption_debug()); } page_res_it.forward(); @@ -308,7 +310,8 @@ bool Tesseract::recog_all_words(PAGE_RES* page_res, page_res_it.restart_page(); word_index = 0; most_recently_used_ = this; - while (!tessedit_test_adaption && page_res_it.word() != NULL) { + while (tessedit_tess_adaption_mode != 0x0 && !tessedit_test_adaption && + page_res_it.word() != NULL) { set_global_loc_code(LOC_PASS2); word_index++; if (monitor != NULL) { @@ -382,17 +385,6 @@ bool Tesseract::recog_all_words(PAGE_RES* page_res, blamer_pass(page_res); } - if (!save_blob_choices) { - // We aren't saving the blob choices so get rid of them now. - // set_blob_choices() does a deep clear. - page_res_it.restart_page(); - while (page_res_it.word() != NULL) { - WERD_RES* word = page_res_it.word(); - word->best_choice->set_blob_choices(NULL); - page_res_it.forward(); - } - } - // Write results pass. set_global_loc_code(LOC_WRITE_RESULTS); // This is now redundant, but retained commented so show how to obtain @@ -436,39 +428,21 @@ void Tesseract::bigram_correction_pass(PAGE_RES *page_res) { continue; } // Two words sharing the same language model, excellent! - if (w->alt_choices.empty()) { - if (tessedit_bigram_debug) { - tprintf("Alt choices not set up for word choice: %s\n", - w->best_choice->unichar_string().string()); - } - continue; - } - if (w_prev->alt_choices.empty()) { - if (tessedit_bigram_debug) { - tprintf("Alt choices not set up for word choice: %s\n", - w_prev->best_choice->unichar_string().string()); - } - continue; - } - - // We saved alternate choices, excellent! GenericVector overrides_word1; - GenericVector *> overrides_word1_state; GenericVector overrides_word2; - GenericVector *> overrides_word2_state; STRING orig_w1_str = w_prev->best_choice->unichar_string(); STRING orig_w2_str = w->best_choice->unichar_string(); WERD_CHOICE prev_best(w->uch_set); { int w1start, w1end; - w_prev->WithoutFootnoteSpan(&w1start, &w1end); + w_prev->best_choice->GetNonSuperscriptSpan(&w1start, &w1end); prev_best = w_prev->best_choice->shallow_copy(w1start, w1end); } WERD_CHOICE this_best(w->uch_set); { int w2start, w2end; - w->WithoutFootnoteSpan(&w2start, &w2end); + w->best_choice->GetNonSuperscriptSpan(&w2start, &w2end); this_best = w->best_choice->shallow_copy(w2start, w2end); } @@ -484,37 +458,36 @@ void Tesseract::bigram_correction_pass(PAGE_RES *page_res) { orig_w1_str.string(), orig_w2_str.string()); } if (tessedit_bigram_debug > 1) { - if (w_prev->alt_choices.size() > 1) { - print_word_alternates_list(w_prev->best_choice, &w_prev->alt_choices); + if (!w_prev->best_choices.singleton()) { + w_prev->PrintBestChoices(); } - if (w->alt_choices.size() > 1) { - print_word_alternates_list(w->best_choice, &w->alt_choices); + if (!w->best_choices.singleton()) { + w->PrintBestChoices(); } } float best_rating = 0.0; int best_idx = 0; - for (int i = 0; i < w_prev->alt_choices.size(); i++) { - WERD_CHOICE *p1 = w_prev->alt_choices.get(i); + WERD_CHOICE_IT prev_it(&w_prev->best_choices); + for (prev_it.mark_cycle_pt(); !prev_it.cycled_list(); prev_it.forward()) { + WERD_CHOICE *p1 = prev_it.data(); WERD_CHOICE strip1(w->uch_set); { int p1start, p1end; - w_prev->WithoutFootnoteSpan(*p1, w_prev->alt_states.get(i), - &p1start, &p1end); + p1->GetNonSuperscriptSpan(&p1start, &p1end); strip1 = p1->shallow_copy(p1start, p1end); } - for (int j = 0; j < w->alt_choices.size(); j++) { - WERD_CHOICE *p2 = w->alt_choices.get(j); + WERD_CHOICE_IT w_it(&w->best_choices); + for (w_it.mark_cycle_pt(); !w_it.cycled_list(); w_it.forward()) { + WERD_CHOICE *p2 = w_it.data(); WERD_CHOICE strip2(w->uch_set); { int p2start, p2end; - w->WithoutFootnoteSpan(*p2, w->alt_states.get(j), &p2start, &p2end); + p2->GetNonSuperscriptSpan(&p2start, &p2end); strip2 = p2->shallow_copy(p2start, p2end); } if (w->tesseract->getDict().valid_bigram(strip1, strip2)) { overrides_word1.push_back(p1); - overrides_word1_state.push_back(&w_prev->alt_states.get(i)); overrides_word2.push_back(p2); - overrides_word2_state.push_back(&w->alt_states.get(j)); if (overrides_word1.size() == 1 || p1->rating() + p2->rating() < best_rating) { best_rating = p1->rating() + p2->rating(); @@ -538,12 +511,10 @@ void Tesseract::bigram_correction_pass(PAGE_RES *page_res) { STRING new_w1_str = overrides_word1[best_idx]->unichar_string(); STRING new_w2_str = overrides_word2[best_idx]->unichar_string(); if (new_w1_str != orig_w1_str) { - w_prev->ReplaceBestChoice(*overrides_word1[best_idx], - *overrides_word1_state[best_idx]); + w_prev->ReplaceBestChoice(overrides_word1[best_idx]); } if (new_w2_str != orig_w2_str) { - w->ReplaceBestChoice(*overrides_word2[best_idx], - *overrides_word2_state[best_idx]); + w->ReplaceBestChoice(overrides_word2[best_idx]); } if (tessedit_bigram_debug > 0) { STRING choices_description; @@ -684,34 +655,8 @@ void Tesseract::blamer_pass(PAGE_RES* page_res) { for (page_res_it.restart_page(); page_res_it.word() != NULL; page_res_it.forward()) { WERD_RES *word = page_res_it.word(); - if (word->blamer_bundle == NULL) { - word->blamer_bundle = new BlamerBundle(); - word->blamer_bundle->incorrect_result_reason = IRR_PAGE_LAYOUT; - word->blamer_bundle->debug = word->blamer_bundle->IncorrectReason(); - word->blamer_bundle->debug += " to blame"; - } else if (word->blamer_bundle->incorrect_result_reason == - IRR_NO_TRUTH) { - word->blamer_bundle->SetBlame(IRR_NO_TRUTH, "Rejected truth", - word->best_choice, wordrec_debug_blamer); - } else { - bool correct = ChoiceIsCorrect(*word->uch_set, word->best_choice, - word->blamer_bundle->truth_text); - IncorrectResultReason irr = - word->blamer_bundle->incorrect_result_reason; - if (irr == IRR_CORRECT && !correct) { - STRING debug = "Choice is incorrect after recognition"; - word->blamer_bundle->SetBlame(IRR_UNKNOWN, debug, - word->best_choice, - wordrec_debug_blamer); - } else if (irr != IRR_CORRECT && correct) { - if (wordrec_debug_blamer) { - tprintf("Corrected %s\n", word->blamer_bundle->debug.string()); - } - word->blamer_bundle->incorrect_result_reason = IRR_CORRECT; - word->blamer_bundle->debug = ""; - } - } - page_res->blame_reasons[word->blamer_bundle->incorrect_result_reason]++; + BlamerBundle::LastChanceBlame(wordrec_debug_blamer, word); + page_res->blame_reasons[word->blamer_bundle->incorrect_result_reason()]++; } tprintf("Blame reasons:\n"); for (int bl = 0; bl < IRR_NUM_REASONS; ++bl) { @@ -730,7 +675,9 @@ void Tesseract::blamer_pass(PAGE_RES* page_res) { // Helper returns true if the new_word is better than the word, using a // simple test of better certainty AND rating (to reduce false positives // from cube) or a dictionary vs non-dictionary word. -static bool NewWordBetter(const WERD_RES& word, const WERD_RES& new_word) { +static bool NewWordBetter(const WERD_RES& word, const WERD_RES& new_word, + double rating_ratio, + double certainty_margin) { if (new_word.best_choice == NULL) { return false; // New one no good. } @@ -742,7 +689,11 @@ static bool NewWordBetter(const WERD_RES& word, const WERD_RES& new_word) { return true; // New word has better confidence. } if (!Dict::valid_word_permuter(word.best_choice->permuter(), false) && - Dict::valid_word_permuter(new_word.best_choice->permuter(), false)) { + Dict::valid_word_permuter(new_word.best_choice->permuter(), false) && + new_word.best_choice->rating() < + word.best_choice->rating() * rating_ratio && + new_word.best_choice->certainty() > + word.best_choice->certainty() - certainty_margin) { return true; // New word is from a dictionary. } return false; // New word is no better. @@ -764,7 +715,9 @@ bool Tesseract::RetryWithLanguage(WERD_RES *word, BLOCK* block, ROW *row, // (to reduce false positives from cube) or a dictionary vs non-dictionary // word. (this->*recognizer)(block, row, &lang_word); - bool new_is_better = NewWordBetter(*word, lang_word); + bool new_is_better = NewWordBetter(*word, lang_word, + classify_max_rating_ratio, + classify_max_certainty_margin); if (classify_debug_level || cube_debug_level) { if (lang_word.best_choice == NULL) { tprintf("New result %s better:%s\n", @@ -793,6 +746,7 @@ void Tesseract::classify_word_and_language(WordRecognizer recognizer, BLOCK* block, ROW *row, WERD_RES *word) { + clock_t start_t = clock(); if (classify_debug_level || cube_debug_level) { tprintf("Processing word with lang %s at:", most_recently_used_->lang.string()); @@ -811,12 +765,15 @@ void Tesseract::classify_word_and_language(WordRecognizer recognizer, if (!word->tess_failed && word->tess_accepted) result_type = "Accepted"; if (classify_debug_level || cube_debug_level) { - tprintf("%s result: %s r=%g, c=%g, accepted=%d, adaptable=%d\n", + tprintf("%s result: %s r=%.4g, c=%.4g, accepted=%d, adaptable=%d" + " xht=[%g,%g]\n", result_type, word->best_choice->unichar_string().string(), word->best_choice->rating(), word->best_choice->certainty(), - word->tess_accepted, word->tess_would_adapt); + word->tess_accepted, word->tess_would_adapt, + word->best_choice->min_x_height(), + word->best_choice->max_x_height()); } if (word->tess_failed || !word->tess_accepted) { // Try all the other languages to see if they are any better. @@ -846,6 +803,12 @@ void Tesseract::classify_word_and_language(WordRecognizer recognizer, } } } + clock_t ocr_t = clock(); + if (tessedit_timing_debug) { + tprintf("%s (ocr took %.2f sec)\n", + word->best_choice->unichar_string().string(), + static_cast(ocr_t-start_t)/CLOCKS_PER_SEC); + } } /** @@ -860,92 +823,25 @@ void Tesseract::classify_word_pass1(BLOCK* block, ROW *row, WERD_RES *word) { cube_word_pass1(block, row, word); return; } - - BLOB_CHOICE_LIST_CLIST *blob_choices = new BLOB_CHOICE_LIST_CLIST(); - BOOL8 adapt_ok; - const char *rejmap; - inT16 index; - STRING mapstr = ""; - - check_debug_pt(word, 0); - if (word->SetupForTessRecognition(unicharset, this, BestPix(), - classify_bln_numeric_mode, - this->textord_use_cjk_fp_model, - row, block)) - tess_segment_pass1(word, blob_choices); - if (!word->tess_failed) { - /* - The adaption step used to be here. It has been moved to after - make_reject_map so that we know whether the word will be accepted in the - first pass or not. This move will PREVENT adaption to words containing - double quotes because the word will not be identical to what tess thinks - its best choice is. (See CurrentBestChoiceIs in - stopper.cpp which is used by AdaptableWord in - adaptmatch.cpp) - */ - - if (!word->word->flag(W_REP_CHAR)) { - // TODO(daria) delete these hacks when replaced by more generic code. - // Convert '' (double single) to " (single double). - word->fix_quotes(blob_choices); - if (tessedit_fix_hyphens) // turn -- to - - word->fix_hyphens(blob_choices); - - word->tess_accepted = tess_acceptable_word(word->best_choice, - word->raw_choice); - - word->tess_would_adapt = word->best_choice && word->raw_choice && - AdaptableWord(word->rebuild_word, - *word->best_choice, - *word->raw_choice); - // Also sets word->done flag - make_reject_map(word, blob_choices, row, 1); - - adapt_ok = word_adaptable(word, tessedit_tess_adaption_mode); - - if (adapt_ok || tessedit_tess_adapt_to_rejmap) { - if (!tessedit_tess_adapt_to_rejmap) { - rejmap = NULL; - } else { - ASSERT_HOST(word->reject_map.length() == - word->best_choice->length()); - - for (index = 0; index < word->reject_map.length(); index++) { - if (adapt_ok || word->reject_map[index].accepted()) - mapstr += '1'; - else - mapstr += '0'; - } - rejmap = mapstr.string(); - } - // Send word to adaptive classifier for training. - word->BestChoiceToCorrectText(); - set_word_fonts(word, blob_choices); - LearnWord(NULL, rejmap, word); - // Mark misadaptions if running blamer. - if (word->blamer_bundle != NULL && - word->blamer_bundle->incorrect_result_reason != IRR_NO_TRUTH && - !ChoiceIsCorrect(*word->uch_set, word->best_choice, - word->blamer_bundle->truth_text)) { - word->blamer_bundle->misadaption_debug ="misadapt to word ("; - word->blamer_bundle->misadaption_debug += - word->best_choice->permuter_name(); - word->blamer_bundle->misadaption_debug += "): "; - word->blamer_bundle->FillDebugString( - "", word->best_choice, &(word->blamer_bundle->misadaption_debug)); - if (wordrec_debug_blamer) { - tprintf("%s\n", word->blamer_bundle->misadaption_debug.string()); - } - } + match_word_pass_n(1, word, row, block); + if (!word->tess_failed && !word->word->flag(W_REP_CHAR)) { + word->tess_would_adapt = AdaptableWord(word); + bool adapt_ok = word_adaptable(word, tessedit_tess_adaption_mode); + + if (adapt_ok) { + // Send word to adaptive classifier for training. + word->BestChoiceToCorrectText(); + LearnWord(NULL, word); + // Mark misadaptions if running blamer. + if (word->blamer_bundle != NULL) { + word->blamer_bundle->SetMisAdaptionDebug(word->best_choice, + wordrec_debug_blamer); } - - if (tessedit_enable_doc_dict) - tess_add_doc_word(word->best_choice); } - } - // Save best choices in the WERD_CHOICE if needed - word->best_choice->set_blob_choices(blob_choices); + if (tessedit_enable_doc_dict && !word->IsAmbiguous()) + tess_add_doc_word(word->best_choice); + } } // Helper to report the result of the xheight fix. @@ -976,7 +872,7 @@ bool Tesseract::TrainedXheightFix(WERD_RES *word, BLOCK* block, ROW *row) { if (original_misfits == 0) return false; float new_x_ht = ComputeCompatibleXheight(word); - if (new_x_ht > 0.0f) { + if (new_x_ht >= kMinRefitXHeightFraction * word->x_height) { WERD_RES new_x_ht_word(word->word); if (word->blamer_bundle != NULL) { new_x_ht_word.blamer_bundle = new BlamerBundle(); @@ -984,7 +880,7 @@ bool Tesseract::TrainedXheightFix(WERD_RES *word, BLOCK* block, ROW *row) { } new_x_ht_word.x_height = new_x_ht; new_x_ht_word.caps_height = 0.0; - match_word_pass2(&new_x_ht_word, row, block); + match_word_pass_n(2, &new_x_ht_word, row, block); if (!new_x_ht_word.tess_failed) { int new_misfits = CountMisfitTops(&new_x_ht_word); if (debug_x_ht_level >= 1) { @@ -1026,26 +922,24 @@ void Tesseract::classify_word_pass2(BLOCK* block, ROW *row, WERD_RES *word) { tessedit_ocr_engine_mode != OEM_TESSERACT_CUBE_COMBINED) return; - bool done_this_pass = false; set_global_subloc_code(SUBLOC_NORM); check_debug_pt(word, 30); if (!word->done || tessedit_training_tess) { word->caps_height = 0.0; if (word->x_height == 0.0f) word->x_height = row->x_height(); - match_word_pass2(word, row, block); - done_this_pass = TRUE; + match_word_pass_n(2, word, row, block); check_debug_pt(word, 40); } + SubAndSuperscriptFix(word); + if (!word->tess_failed && !word->word->flag(W_REP_CHAR)) { - bool accept_new_xht = false; - if (unicharset.top_bottom_useful() && unicharset.script_has_xheight()) { + if (unicharset.top_bottom_useful() && unicharset.script_has_xheight() && + block->classify_rotation().y() == 0.0f) { // Use the tops and bottoms since they are available. - accept_new_xht = TrainedXheightFix(word, block, row); + TrainedXheightFix(word, block, row); } - if (accept_new_xht) - done_this_pass = true; // Test for small caps. Word capheight must be close to block xheight, // and word must contain no lower case letters, and at least one upper case. double small_cap_xheight = block->x_height() * kXHeightCapRatio; @@ -1092,60 +986,38 @@ void Tesseract::classify_word_pass2(BLOCK* block, ROW *row, WERD_RES *word) { * Baseline normalize the word and pass it to Tess. */ -void Tesseract::match_word_pass2(WERD_RES *word, //word to do - ROW *row, - BLOCK* block) { - BLOB_CHOICE_LIST_CLIST *blob_choices = new BLOB_CHOICE_LIST_CLIST(); - +void Tesseract::match_word_pass_n(int pass_n, WERD_RES *word, + ROW *row, BLOCK* block) { if (word->SetupForTessRecognition(unicharset, this, BestPix(), classify_bln_numeric_mode, - this->textord_use_cjk_fp_model, + textord_use_cjk_fp_model, + poly_allow_detailed_fx, row, block)) - tess_segment_pass2(word, blob_choices); + tess_segment_pass_n(pass_n, word); if (!word->tess_failed) { if (!word->word->flag (W_REP_CHAR)) { - word->fix_quotes(blob_choices); + word->fix_quotes(); if (tessedit_fix_hyphens) - word->fix_hyphens(blob_choices); + word->fix_hyphens(); /* Dont trust fix_quotes! - though I think I've fixed the bug */ - if (word->best_choice->length() != word->box_word->length() || - word->best_choice->length() != blob_choices->length()) { + if (word->best_choice->length() != word->box_word->length()) { tprintf("POST FIX_QUOTES FAIL String:\"%s\"; Strlen=%d;" - " #Blobs=%d; #Choices=%d\n", + " #Blobs=%d\n", word->best_choice->debug_string().string(), word->best_choice->length(), - word->box_word->length(), blob_choices->length()); + word->box_word->length()); } - word->tess_accepted = tess_acceptable_word(word->best_choice, - word->raw_choice); + word->tess_accepted = tess_acceptable_word(word); - make_reject_map (word, blob_choices, row, 2); + // Also sets word->done flag + make_reject_map(word, row, pass_n); } } + set_word_fonts(word); - // Save best choices in the WERD_CHOICE if needed - word->best_choice->set_blob_choices(blob_choices); - set_word_fonts(word, blob_choices); - - assert (word->raw_choice != NULL); -} - -// Helper to find the BLOB_CHOICE in the bc_list that matches the given -// unichar_id, or NULL if there is no match. -static BLOB_CHOICE* FindMatchingChoice(UNICHAR_ID char_id, - BLOB_CHOICE_LIST* bc_list) { - // Find the corresponding best BLOB_CHOICE. - BLOB_CHOICE_IT choice_it(bc_list); - for (choice_it.mark_cycle_pt(); !choice_it.cycled_list(); - choice_it.forward()) { - BLOB_CHOICE* choice = choice_it.data(); - if (choice->unichar_id() == char_id) { - return choice; - } - } - return NULL; + ASSERT_HOST(word->raw_choice != NULL); } // Helper to return the best rated BLOB_CHOICE in the whole word that matches @@ -1154,9 +1026,9 @@ static BLOB_CHOICE* FindBestMatchingChoice(UNICHAR_ID char_id, WERD_RES* word_res) { // Find the corresponding best BLOB_CHOICE from any position in the word_res. BLOB_CHOICE* best_choice = NULL; - BLOB_CHOICE_LIST_C_IT bc_it(word_res->best_choice->blob_choices()); - for (bc_it.mark_cycle_pt(); !bc_it.cycled_list(); bc_it.forward()) { - BLOB_CHOICE* choice = FindMatchingChoice(char_id, bc_it.data()); + for (int i = 0; i < word_res->best_choice->length(); ++i) { + BLOB_CHOICE* choice = FindMatchingChoice(char_id, + word_res->GetBlobChoices(i)); if (choice != NULL) { if (best_choice == NULL || choice->rating() < best_choice->rating()) best_choice = choice; @@ -1171,12 +1043,11 @@ static BLOB_CHOICE* FindBestMatchingChoice(UNICHAR_ID char_id, static void CorrectRepcharChoices(BLOB_CHOICE* blob_choice, WERD_RES* word_res) { WERD_CHOICE* word = word_res->best_choice; - BLOB_CHOICE_LIST_C_IT bc_it(word->blob_choices()); - for (bc_it.mark_cycle_pt(); !bc_it.cycled_list(); bc_it.forward()) { + for (int i = 0; i < word_res->best_choice->length(); ++i) { BLOB_CHOICE* choice = FindMatchingChoice(blob_choice->unichar_id(), - bc_it.data()); + word_res->GetBlobChoices(i)); if (choice == NULL) { - BLOB_CHOICE_IT choice_it(bc_it.data()); + BLOB_CHOICE_IT choice_it(word_res->GetBlobChoices(i)); choice_it.add_before_stay_put(new BLOB_CHOICE(*blob_choice)); } } @@ -1267,7 +1138,8 @@ void Tesseract::ExplodeRepeatedWord(BLOB_CHOICE* best_choice, // Setup the single char WERD_RES if (rep_word->SetupForTessRecognition(*word_res->uch_set, this, BestPix(), false, - this->textord_use_cjk_fp_model, + textord_use_cjk_fp_model, + poly_allow_detailed_fx, page_res_it->row()->row, page_res_it->block()->block)) { rep_word->CloneChoppedToRebuild(); @@ -1494,16 +1366,14 @@ static void find_modal_font( //good chars in word * * Get the fonts for the word. */ -void Tesseract::set_word_fonts(WERD_RES *word, - BLOB_CHOICE_LIST_CLIST *blob_choices) { - if (blob_choices == NULL) return; +void Tesseract::set_word_fonts(WERD_RES *word) { // Don't try to set the word fonts for a cube word, as the configs // will be meaningless. if (word->chopped_word == NULL) return; + ASSERT_HOST(word->best_choice != NULL); inT32 index; // char id index // character iterator - BLOB_CHOICE_LIST_C_IT char_it = blob_choices; BLOB_CHOICE_IT choice_it; // choice iterator int fontinfo_size = get_fontinfo_table().size(); int fontset_size = get_fontset_table().size(); @@ -1516,10 +1386,9 @@ void Tesseract::set_word_fonts(WERD_RES *word, word->best_choice_fontinfo_ids.clear(); } // Compute the modal font for the word - for (char_it.mark_cycle_pt(), index = 0; - !char_it.cycled_list(); ++index, char_it.forward()) { + for (index = 0; index < word->best_choice->length(); ++index) { UNICHAR_ID word_ch_id = word->best_choice->unichar_id(index); - choice_it.set_to_list(char_it.data()); + choice_it.set_to_list(word->GetBlobChoices(index)); if (tessedit_debug_fonts) { tprintf("Examining fonts in %s\n", word->best_choice->debug_string().string()); diff --git a/ccmain/cube_control.cpp b/ccmain/cube_control.cpp index 5b222a12b1..411ea1a511 100644 --- a/ccmain/cube_control.cpp +++ b/ccmain/cube_control.cpp @@ -144,54 +144,6 @@ bool Tesseract::create_cube_box_word(Boxa *char_boxes, return true; } -/********************************************************************** - * create_werd_choice - * - **********************************************************************/ -static WERD_CHOICE *create_werd_choice( - CharSamp** char_samples, - int num_chars, - const char* str, - float certainty, - const UNICHARSET &unicharset, - CharSet* cube_char_set - ) { - // Insert unichar ids into WERD_CHOICE - WERD_CHOICE *werd_choice = new WERD_CHOICE(&unicharset, num_chars); - // within a word, cube recognizes the word in reading order. - werd_choice->set_unichars_in_script_order(true); - ASSERT_HOST(werd_choice != NULL); - UNICHAR_ID uch_id; - for (int i = 0; i < num_chars; ++i) { - uch_id = cube_char_set->UnicharID(char_samples[i]->StrLabel()); - if (uch_id != INVALID_UNICHAR_ID) - werd_choice->append_unichar_id_space_allocated( - uch_id, 1, 0.0, certainty); - } - - BLOB_CHOICE *blob_choice; - BLOB_CHOICE_LIST *choices_list; - BLOB_CHOICE_IT choices_list_it; - BLOB_CHOICE_LIST_CLIST *blob_choices = new BLOB_CHOICE_LIST_CLIST(); - BLOB_CHOICE_LIST_C_IT blob_choices_it; - blob_choices_it.set_to_list(blob_choices); - - for (int i = 0; i < werd_choice->length(); ++i) { - // Create new BLOB_CHOICE_LIST for this unichar - choices_list = new BLOB_CHOICE_LIST(); - choices_list_it.set_to_list(choices_list); - // Add a single BLOB_CHOICE to the list - blob_choice = new BLOB_CHOICE(werd_choice->unichar_id(i), - 0.0, certainty, -1, -1, 0, 0, 0, false); - choices_list_it.add_after_then_move(blob_choice); - // Add list to the clist - blob_choices_it.add_to_end(choices_list); - } - werd_choice->set_certainty(certainty); - werd_choice->set_blob_choices(blob_choices); - return werd_choice; -} - /********************************************************************** * init_cube_objects * @@ -419,29 +371,32 @@ bool Tesseract::cube_recognize(CubeObject *cube_obj, BLOCK* block, return false; } - // Create cube's best choice. - WERD_CHOICE* cube_werd_choice = create_werd_choice( - char_samples, num_chars, cube_best_str.c_str(), cube_certainty, - unicharset, cube_cntxt_->CharacterSet()); - delete []char_samples; + // Fill tesseract result's fields with cube results + fill_werd_res(cube_box_word, cube_best_str.c_str(), word); - if (!cube_werd_choice) { - if (cube_debug_level > 0) { - tprintf("Cube WARNING (Tesseract::cube_recognize): Could not " - "create cube WERD_CHOICE\n"); - } - word->SetupFake(unicharset); - return false; + // Create cube's best choice. + BLOB_CHOICE** choices = new BLOB_CHOICE*[num_chars]; + for (int i = 0; i < num_chars; ++i) { + UNICHAR_ID uch_id = + cube_cntxt_->CharacterSet()->UnicharID(char_samples[i]->StrLabel()); + choices[i] = new BLOB_CHOICE(uch_id, 0.0, cube_certainty, -1, -1, + 0, 0, 0, 0, BCC_STATIC_CLASSIFIER); } + word->FakeClassifyWord(num_chars, choices); + // within a word, cube recognizes the word in reading order. + word->best_choice->set_unichars_in_script_order(true); + delete [] choices; + delete [] char_samples; + + // Some sanity checks + ASSERT_HOST(word->best_choice->length() == word->reject_map.length()); + if (cube_debug_level || classify_debug_level) { tprintf("Cube result: %s r=%g, c=%g\n", - cube_werd_choice->unichar_string().string(), - cube_werd_choice->rating(), - cube_werd_choice->certainty()); + word->best_choice->unichar_string().string(), + word->best_choice->rating(), + word->best_choice->certainty()); } - - // Fill tesseract result's fields with cube results - fill_werd_res(cube_box_word, cube_werd_choice, cube_best_str.c_str(), word); return true; } @@ -452,13 +407,8 @@ bool Tesseract::cube_recognize(CubeObject *cube_obj, BLOCK* block, * **********************************************************************/ void Tesseract::fill_werd_res(const BoxWord& cube_box_word, - WERD_CHOICE* cube_werd_choice, const char* cube_best_str, WERD_RES* tess_werd_res) { - // Replace tesseract results's best choice with cube's - tess_werd_res->best_choice = cube_werd_choice; - tess_werd_res->raw_choice = new WERD_CHOICE(*cube_werd_choice); - delete tess_werd_res->box_word; tess_werd_res->box_word = new BoxWord(cube_box_word); tess_werd_res->box_word->ClipToOriginalWord(tess_werd_res->denorm.block(), @@ -466,23 +416,13 @@ void Tesseract::fill_werd_res(const BoxWord& cube_box_word, // Fill text and remaining fields tess_werd_res->word->set_text(cube_best_str); tess_werd_res->tess_failed = FALSE; - tess_werd_res->tess_accepted = - tess_acceptable_word(tess_werd_res->best_choice, - tess_werd_res->raw_choice); + tess_werd_res->tess_accepted = tess_acceptable_word(tess_werd_res); // There is no output word, so we can' call AdaptableWord, but then I don't // think we need to. Fudge the result with accepted. tess_werd_res->tess_would_adapt = tess_werd_res->tess_accepted; - // Initialize the reject_map and set it to done, i.e., ignore all of - // tesseract's tests for rejection - tess_werd_res->reject_map.initialise(cube_werd_choice->length()); + // Set word to done, i.e., ignore all of tesseract's tests for rejection tess_werd_res->done = tess_werd_res->tess_accepted; - - // Some sanity checks - ASSERT_HOST(tess_werd_res->best_choice->length() == - tess_werd_res->best_choice->blob_choices()->length()); - ASSERT_HOST(tess_werd_res->best_choice->length() == - tess_werd_res->reject_map.length()); } } // namespace tesseract diff --git a/ccmain/docqual.cpp b/ccmain/docqual.cpp index f771013321..68a491ce3b 100644 --- a/ccmain/docqual.cpp +++ b/ccmain/docqual.cpp @@ -23,7 +23,6 @@ #include #include "docqual.h" -#include "tfacep.h" #include "reject.h" #include "tesscallback.h" #include "tessvars.h" @@ -66,7 +65,7 @@ struct DocQualCallbacks { *************************************************************************/ inT16 Tesseract::word_blob_quality(WERD_RES *word, ROW *row) { if (word->bln_boxes == NULL || - word->rebuild_word == NULL || word->rebuild_word->blobs == NULL) + word->rebuild_word == NULL || word->rebuild_word->blobs.empty()) return 0; DocQualCallbacks cb(word); @@ -81,8 +80,8 @@ inT16 Tesseract::word_outline_errs(WERD_RES *word) { inT16 err_count = 0; if (word->rebuild_word != NULL) { - TBLOB* blob = word->rebuild_word->blobs; - for (; blob != NULL; blob = blob->next) { + for (int b = 0; b < word->rebuild_word->NumBlobs(); ++b) { + TBLOB* blob = word->rebuild_word->blobs[b]; err_count += count_outline_errs(word->best_choice->unichar_string()[i], blob->NumOutlines()); i++; @@ -101,7 +100,7 @@ void Tesseract::word_char_quality(WERD_RES *word, inT16 *match_count, inT16 *accepted_match_count) { if (word->bln_boxes == NULL || - word->rebuild_word == NULL || word->rebuild_word->blobs == NULL) + word->rebuild_word == NULL || word->rebuild_word->blobs.empty()) return; DocQualCallbacks cb(word); @@ -118,7 +117,7 @@ void Tesseract::word_char_quality(WERD_RES *word, *************************************************************************/ void Tesseract::unrej_good_chs(WERD_RES *word, ROW *row) { if (word->bln_boxes == NULL || - word->rebuild_word == NULL || word->rebuild_word->blobs == NULL) + word->rebuild_word == NULL || word->rebuild_word->blobs.empty()) return; DocQualCallbacks cb(word); @@ -990,7 +989,8 @@ BOOL8 Tesseract::noise_outlines(TWERD *word) { inT16 max_dimension; float small_limit = kBlnXHeight * crunch_small_outlines_size; - for (TBLOB* blob = word->blobs; blob != NULL; blob = blob->next) { + for (int b = 0; b < word->NumBlobs(); ++b) { + TBLOB* blob = word->blobs[b]; for (TESSLINE* ol = blob->outlines; ol != NULL; ol = ol->next) { outline_count++; box = ol->bounding_box(); @@ -1002,6 +1002,7 @@ BOOL8 Tesseract::noise_outlines(TWERD *word) { small_outline_count++; } } - return (small_outline_count >= outline_count); + return small_outline_count >= outline_count; } + } // namespace tesseract diff --git a/ccmain/equationdetect.cpp b/ccmain/equationdetect.cpp index d4a1fc3f5d..0ab7e7acfe 100644 --- a/ccmain/equationdetect.cpp +++ b/ccmain/equationdetect.cpp @@ -19,7 +19,7 @@ #ifdef _MSC_VER #pragma warning(disable:4244) // Conversion warnings -#include "mathfix.h" +#include #endif #ifdef __MINGW32__ @@ -173,21 +173,21 @@ void EquationDetect::IdentifySpecialText( BLOB_CHOICE_LIST ratings_equ, ratings_lang; C_BLOB* blob = blobnbox->cblob(); - TBLOB* tblob = TBLOB::PolygonalCopy(blob); + // TODO(joeliu/rays) Fix this. We may have to normalize separately for + // each classifier here, as they may require different PolygonalCopy. + TBLOB* tblob = TBLOB::PolygonalCopy(false, blob); const TBOX& box = tblob->bounding_box(); // Normalize the blob. Set the origin to the place we want to be the // bottom-middle, and scaling is to make the height the x-height. float scaling = static_cast(kBlnXHeight) / box.height(); - DENORM denorm; float x_orig = (box.left() + box.right()) / 2.0f, y_orig = box.bottom(); - denorm.SetupNormalization(NULL, NULL, NULL, NULL, NULL, 0, - x_orig, y_orig, scaling, scaling, - 0.0f, static_cast(kBlnBaselineOffset)); TBLOB* normed_blob = new TBLOB(*tblob); - normed_blob->Normalize(denorm); - equ_tesseract_->AdaptiveClassifier(normed_blob, denorm, &ratings_equ, NULL); - lang_tesseract_->AdaptiveClassifier(normed_blob, denorm, &ratings_lang, NULL); + normed_blob->Normalize(NULL, NULL, NULL, x_orig, y_orig, scaling, scaling, + 0.0f, static_cast(kBlnBaselineOffset), + false, NULL); + equ_tesseract_->AdaptiveClassifier(normed_blob, &ratings_equ, NULL); + lang_tesseract_->AdaptiveClassifier(normed_blob, &ratings_lang, NULL); delete normed_blob; delete tblob; diff --git a/ccmain/fixspace.cpp b/ccmain/fixspace.cpp index 1c253f07b5..ec568720cd 100644 --- a/ccmain/fixspace.cpp +++ b/ccmain/fixspace.cpp @@ -35,6 +35,7 @@ #define MAXSPACING 128 /*max expected spacing in pix */ namespace tesseract { + /** * @name fix_fuzzy_spaces() * Walk over the page finding sequences of words joined by fuzzy spaces. Extract @@ -183,7 +184,7 @@ void initialise_search(WERD_RES_LIST &src_list, WERD_RES_LIST &new_list) { for (src_it.mark_cycle_pt(); !src_it.cycled_list(); src_it.forward()) { src_wd = src_it.data(); if (!src_wd->combination) { - new_wd = new WERD_RES(*src_wd); + new_wd = WERD_RES::deep_copy(src_wd); new_wd->combination = FALSE; new_wd->part_of_combo = FALSE; new_it.add_after_then_move(new_wd); @@ -502,86 +503,6 @@ void Tesseract::dump_words(WERD_RES_LIST &perm, inT16 score, } } - -/** - * @name uniformly_spaced() - * Return true if one of the following are true: - * - All inter-char gaps are the same width - * - The largest gap is no larger than twice the mean/median of the others - * - The largest gap is < normalised_max_nonspace - * **** REMEMBER - WE'RE NOW WORKING WITH A BLN WERD !!! - */ -BOOL8 Tesseract::uniformly_spaced(WERD_RES *word) { - TBOX box; - inT16 prev_right = -MAX_INT16; - inT16 gap; - inT16 max_gap = -MAX_INT16; - inT16 max_gap_count = 0; - STATS gap_stats(0, MAXSPACING); - BOOL8 result; - const ROW *row = word->denorm.row(); - float max_non_space; - float normalised_max_nonspace; - inT16 i = 0; - inT16 offset = 0; - STRING punct_chars = "\"`',.:;"; - - for (TBLOB* blob = word->rebuild_word->blobs; blob != NULL; - blob = blob->next) { - box = blob->bounding_box(); - if ((prev_right > -MAX_INT16) && - (!punct_chars.contains( - word->best_choice->unichar_string() - [offset - word->best_choice->unichar_lengths()[i - 1]]) && - !punct_chars.contains( - word->best_choice->unichar_string()[offset]))) { - gap = box.left() - prev_right; - if (gap < max_gap) { - gap_stats.add(gap, 1); - } else if (gap == max_gap) { - max_gap_count++; - } else { - if (max_gap_count > 0) - gap_stats.add(max_gap, max_gap_count); - max_gap = gap; - max_gap_count = 1; - } - } - prev_right = box.right(); - offset += word->best_choice->unichar_lengths()[i++]; - } - - max_non_space = (row->space() + 3 * row->kern()) / 4; - normalised_max_nonspace = max_non_space * kBlnXHeight / row->x_height(); - - result = ( - gap_stats.get_total() == 0 || - max_gap <= normalised_max_nonspace || - (gap_stats.get_total() > 2 && max_gap <= 2 * gap_stats.median()) || - (gap_stats.get_total() <= 2 && max_gap <= 2 * gap_stats.mean())); - #ifndef SECURE_NAMES - if ((debug_fix_space_level > 1)) { - if (result) { - tprintf( - "ACCEPT SPACING FOR: \"%s\" norm_maxnon = %f max=%d maxcount=%d " - "total=%d mean=%f median=%f\n", - word->best_choice->unichar_string().string(), normalised_max_nonspace, - max_gap, max_gap_count, gap_stats.get_total(), gap_stats.mean(), - gap_stats.median()); - } else { - tprintf( - "REJECT SPACING FOR: \"%s\" norm_maxnon = %f max=%d maxcount=%d " - "total=%d mean=%f median=%f\n", - word->best_choice->unichar_string().string(), normalised_max_nonspace, - max_gap, max_gap_count, gap_stats.get_total(), gap_stats.mean(), - gap_stats.median()); - } - } - #endif - - return result; -} - BOOL8 Tesseract::fixspace_thinks_word_done(WERD_RES *word) { if (word->done) return TRUE; @@ -655,7 +576,6 @@ void Tesseract::fix_noisy_space_list(WERD_RES_LIST &best_perm, ROW *row, WERD_RES_LIST current_perm; WERD_RES_IT current_perm_it(¤t_perm); WERD_RES *old_word_res; - WERD_RES *new_word_res; inT16 current_score; BOOL8 improved = FALSE; @@ -663,12 +583,12 @@ void Tesseract::fix_noisy_space_list(WERD_RES_LIST &best_perm, ROW *row, dump_words(best_perm, best_score, 1, improved); - new_word_res = new WERD_RES; old_word_res = best_perm_it.data(); + // Even deep_copy doesn't copy the underlying WERD unless its combination + // flag is true!. old_word_res->combination = TRUE; // Kludge to force deep copy - *new_word_res = *old_word_res; // deep copy + current_perm_it.add_to_end(WERD_RES::deep_copy(old_word_res)); old_word_res->combination = FALSE; // Undo kludge - current_perm_it.add_to_end(new_word_res); break_noisiest_blob_word(current_perm); @@ -774,7 +694,6 @@ inT16 Tesseract::worst_noise_blob(WERD_RES *word_res, if (word_res->rebuild_word == NULL) return -1; // Can't handle cube words. - TBLOB* blob = word_res->rebuild_word->blobs; // Normalised. int blob_count = word_res->box_word->length(); ASSERT_HOST(blob_count <= 512); @@ -789,7 +708,8 @@ inT16 Tesseract::worst_noise_blob(WERD_RES *word_res, word_res->best_choice->unichar_string().string()); #endif - for (i = 0; i < blob_count && blob != NULL; i++, blob = blob->next) { + for (i = 0; i < blob_count && i < word_res->rebuild_word->NumBlobs(); i++) { + TBLOB* blob = word_res->rebuild_word->blobs[i]; if (word_res->reject_map[i].accepted()) noise_score[i] = non_noise_limit; else @@ -929,10 +849,10 @@ inT16 Tesseract::fp_eval_word_spacing(WERD_RES_LIST &word_res_list) { word->best_choice->permuter() == FREQ_DAWG_PERM || word->best_choice->permuter() == USER_DAWG_PERM || safe_dict_word(word) > 0) { - TBLOB* blob = word->rebuild_word->blobs; + int num_blobs = word->rebuild_word->NumBlobs(); UNICHAR_ID space = word->uch_set->unichar_to_id(" "); - for (i = 0; i < word->best_choice->length() && blob != NULL; - ++i, blob = blob->next) { + for (i = 0; i < word->best_choice->length() && i < num_blobs; ++i) { + TBLOB* blob = word->rebuild_word->blobs[i]; if (word->best_choice->unichar_id(i) == space || blob_noise_score(blob) < small_limit) { score -= 1; // penalise possibly erroneous non-space diff --git a/ccmain/fixxht.cpp b/ccmain/fixxht.cpp index 3eecc7971d..b82f0ca503 100644 --- a/ccmain/fixxht.cpp +++ b/ccmain/fixxht.cpp @@ -62,9 +62,9 @@ const int kMaxCharTopRange = 48; // Returns the number of misfit blob tops in this word. int Tesseract::CountMisfitTops(WERD_RES *word_res) { int bad_blobs = 0; - TBLOB* blob = word_res->rebuild_word->blobs; - int blob_id = 0; - for (; blob != NULL; blob = blob->next, ++blob_id) { + int num_blobs = word_res->rebuild_word->NumBlobs(); + for (int blob_id = 0; blob_id < num_blobs; ++blob_id) { + TBLOB* blob = word_res->rebuild_word->blobs[blob_id]; UNICHAR_ID class_id = word_res->best_choice->unichar_id(blob_id); if (unicharset.get_isalpha(class_id) || unicharset.get_isdigit(class_id)) { int top = blob->bounding_box().top(); @@ -94,9 +94,9 @@ int Tesseract::CountMisfitTops(WERD_RES *word_res) { // See comment above for overall algorithm. float Tesseract::ComputeCompatibleXheight(WERD_RES *word_res) { STATS top_stats(0, MAX_UINT8); - TBLOB* blob = word_res->rebuild_word->blobs; - int blob_id = 0; - for (; blob != NULL; blob = blob->next, ++blob_id) { + int num_blobs = word_res->rebuild_word->NumBlobs(); + for (int blob_id = 0; blob_id < num_blobs; ++blob_id) { + TBLOB* blob = word_res->rebuild_word->blobs[blob_id]; UNICHAR_ID class_id = word_res->best_choice->unichar_id(blob_id); if (unicharset.get_isalpha(class_id) || unicharset.get_isdigit(class_id)) { int top = blob->bounding_box().top(); diff --git a/ccmain/imgscale.cpp b/ccmain/imgscale.cpp index 4cb498f731..873f91f570 100644 --- a/ccmain/imgscale.cpp +++ b/ccmain/imgscale.cpp @@ -33,7 +33,7 @@ #include #include -#include "errcode.h" +#include "globaloc.h" // For err_exit. #define f(xc, yc) ((xc - factor*yc)*(xc - factor*yc)) diff --git a/ccmain/ltrresultiterator.cpp b/ccmain/ltrresultiterator.cpp index a754e2ef22..66b29aa51c 100644 --- a/ccmain/ltrresultiterator.cpp +++ b/ccmain/ltrresultiterator.cpp @@ -132,23 +132,7 @@ float LTRResultIterator::Confidence(PageIteratorLevel level) const { ++certainty_count; break; case RIL_SYMBOL: - BLOB_CHOICE_LIST_CLIST* choices = best_choice->blob_choices(); - if (choices != NULL) { - BLOB_CHOICE_LIST_C_IT blob_choices_it(choices); - for (int blob = 0; blob < blob_index_; ++blob) - blob_choices_it.forward(); - BLOB_CHOICE_IT choice_it(blob_choices_it.data()); - for (choice_it.mark_cycle_pt(); - !choice_it.cycled_list(); - choice_it.forward()) { - if (choice_it.data()->unichar_id() == - best_choice->unichar_id(blob_index_)) - break; - } - mean_certainty += choice_it.data()->certainty(); - } else { - mean_certainty += best_choice->certainty(); - } + mean_certainty += best_choice->certainty(blob_index_); ++certainty_count; } if (certainty_count > 0) { @@ -237,55 +221,83 @@ bool LTRResultIterator::WordIsNumeric() const { // Returns true if the word contains blamer information. bool LTRResultIterator::HasBlamerInfo() const { - return (it_->word() != NULL && it_->word()->blamer_bundle != NULL && - (it_->word()->blamer_bundle->debug.length() > 0 || - it_->word()->blamer_bundle->misadaption_debug.length() > 0)); + return it_->word() != NULL && it_->word()->blamer_bundle != NULL && + it_->word()->blamer_bundle->HasDebugInfo(); } // Returns the pointer to ParamsTrainingBundle stored in the BlamerBundle // of the current word. -void *LTRResultIterator::GetParamsTrainingBundle() const { +const void *LTRResultIterator::GetParamsTrainingBundle() const { return (it_->word() != NULL && it_->word()->blamer_bundle != NULL) ? - &(it_->word()->blamer_bundle->params_training_bundle) : NULL; + &(it_->word()->blamer_bundle->params_training_bundle()) : NULL; } // Returns the pointer to the string with blamer information for this word. // Assumes that the word's blamer_bundle is not NULL. const char *LTRResultIterator::GetBlamerDebug() const { - return it_->word()->blamer_bundle->debug.string(); + return it_->word()->blamer_bundle->debug().string(); } // Returns the pointer to the string with misadaption information for this word. // Assumes that the word's blamer_bundle is not NULL. const char *LTRResultIterator::GetBlamerMisadaptionDebug() const { - return it_->word()->blamer_bundle->misadaption_debug.string(); + return it_->word()->blamer_bundle->misadaption_debug().string(); +} + +// Returns true if a truth string was recorded for the current word. +bool LTRResultIterator::HasTruthString() const { + if (it_->word() == NULL) return false; // Already at the end! + if (it_->word()->blamer_bundle == NULL || + it_->word()->blamer_bundle->NoTruth()) { + return false; // no truth information for this word + } + return true; +} + +// Returns true if the given string is equivalent to the truth string for +// the current word. +bool LTRResultIterator::EquivalentToTruth(const char *str) const { + if (!HasTruthString()) return false; + ASSERT_HOST(it_->word()->uch_set != NULL); + WERD_CHOICE str_wd(str, *(it_->word()->uch_set)); + return it_->word()->blamer_bundle->ChoiceIsCorrect(&str_wd); } // Returns the null terminated UTF-8 encoded truth string for the current word. // Use delete [] to free after use. char* LTRResultIterator::WordTruthUTF8Text() const { - if (it_->word() == NULL) return NULL; // Already at the end! - if (it_->word()->blamer_bundle == NULL || - it_->word()->blamer_bundle->incorrect_result_reason == IRR_NO_TRUTH) { - return NULL; // no truth information for this word - } - const GenericVector &truth_vec = - it_->word()->blamer_bundle->truth_text; - STRING truth_text; - for (int i = 0; i < truth_vec.size(); ++i) truth_text += truth_vec[i]; + if (!HasTruthString()) return NULL; + STRING truth_text = it_->word()->blamer_bundle->TruthString(); int length = truth_text.length() + 1; char* result = new char[length]; strncpy(result, truth_text.string(), length); return result; } +// Returns the null terminated UTF-8 encoded normalized OCR string for the +// current word. Use delete [] to free after use. +char* LTRResultIterator::WordNormedUTF8Text() const { + if (it_->word() == NULL) return NULL; // Already at the end! + STRING ocr_text; + WERD_CHOICE* best_choice = it_->word()->best_choice; + const UNICHARSET *unicharset = it_->word()->uch_set; + ASSERT_HOST(best_choice != NULL); + for (int i = 0; i < best_choice->length(); ++i) { + ocr_text += unicharset->get_normed_unichar(best_choice->unichar_id(i)); + } + int length = ocr_text.length() + 1; + char* result = new char[length]; + strncpy(result, ocr_text.string(), length); + return result; +} + // Returns a pointer to serialized choice lattice. // Fills lattice_size with the number of bytes in lattice data. const char *LTRResultIterator::WordLattice(int *lattice_size) const { if (it_->word() == NULL) return NULL; // Already at the end! if (it_->word()->blamer_bundle == NULL) return NULL; - *lattice_size = it_->word()->blamer_bundle->lattice_size; - return it_->word()->blamer_bundle->lattice_data; + *lattice_size = it_->word()->blamer_bundle->lattice_size(); + return it_->word()->blamer_bundle->lattice_data(); } // Returns true if the current symbol is a superscript. @@ -293,7 +305,8 @@ const char *LTRResultIterator::WordLattice(int *lattice_size) const { // this will return the attributes of the first symbol in that word. bool LTRResultIterator::SymbolIsSuperscript() const { if (cblob_it_ == NULL && it_->word() != NULL) - return it_->word()->box_word->BlobPosition(blob_index_) == SP_SUPERSCRIPT; + return it_->word()->best_choice->BlobPosition(blob_index_) == + SP_SUPERSCRIPT; return false; } @@ -302,7 +315,7 @@ bool LTRResultIterator::SymbolIsSuperscript() const { // this will return the attributes of the first symbol in that word. bool LTRResultIterator::SymbolIsSubscript() const { if (cblob_it_ == NULL && it_->word() != NULL) - return it_->word()->box_word->BlobPosition(blob_index_) == SP_SUBSCRIPT; + return it_->word()->best_choice->BlobPosition(blob_index_) == SP_SUBSCRIPT; return false; } @@ -311,7 +324,7 @@ bool LTRResultIterator::SymbolIsSubscript() const { // this will return the attributes of the first symbol in that word. bool LTRResultIterator::SymbolIsDropcap() const { if (cblob_it_ == NULL && it_->word() != NULL) - return it_->word()->box_word->BlobPosition(blob_index_) == SP_DROPCAP; + return it_->word()->best_choice->BlobPosition(blob_index_) == SP_DROPCAP; return false; } @@ -319,13 +332,11 @@ ChoiceIterator::ChoiceIterator(const LTRResultIterator& result_it) { ASSERT_HOST(result_it.it_->word() != NULL); word_res_ = result_it.it_->word(); PAGE_RES_IT res_it(*result_it.it_); - WERD_CHOICE* best_choice = word_res_->best_choice; - BLOB_CHOICE_LIST_CLIST* choices = best_choice->blob_choices(); + BLOB_CHOICE_LIST* choices = NULL; + if (word_res_->ratings != NULL) + choices = word_res_->GetBlobChoices(result_it.blob_index_); if (choices != NULL && !choices->empty()) { - BLOB_CHOICE_LIST_C_IT blob_choices_it(choices); - for (int blob = 0; blob < result_it.blob_index_; ++blob) - blob_choices_it.forward(); - choice_it_ = new BLOB_CHOICE_IT(blob_choices_it.data()); + choice_it_ = new BLOB_CHOICE_IT(choices); choice_it_->mark_cycle_pt(); } else { choice_it_ = NULL; diff --git a/ccmain/ltrresultiterator.h b/ccmain/ltrresultiterator.h index 9464aaed54..89b3c496f5 100644 --- a/ccmain/ltrresultiterator.h +++ b/ccmain/ltrresultiterator.h @@ -23,7 +23,7 @@ #include "platform.h" #include "pageiterator.h" -#include "unicharset.h" +#include "unichar.h" class BLOB_CHOICE_IT; class WERD_RES; @@ -128,7 +128,7 @@ class TESS_API LTRResultIterator : public PageIterator { // Returns the pointer to ParamsTrainingBundle stored in the BlamerBundle // of the current word. - void *GetParamsTrainingBundle() const; + const void *GetParamsTrainingBundle() const; // Returns a pointer to the string with blamer information for this word. // Assumes that the word's blamer_bundle is not NULL. @@ -138,10 +138,21 @@ class TESS_API LTRResultIterator : public PageIterator { // Assumes that the word's blamer_bundle is not NULL. const char *GetBlamerMisadaptionDebug() const; + // Returns true if a truth string was recorded for the current word. + bool HasTruthString() const; + + // Returns true if the given string is equivalent to the truth string for + // the current word. + bool EquivalentToTruth(const char *str) const; + // Returns a null terminated UTF-8 encoded truth string for the current word. // Use delete [] to free after use. char* WordTruthUTF8Text() const; + // Returns a null terminated UTF-8 encoded normalized OCR string for the + // current word. Use delete [] to free after use. + char* WordNormedUTF8Text() const; + // Returns a pointer to serialized choice lattice. // Fills lattice_size with the number of bytes in lattice data. const char *WordLattice(int *lattice_size) const; diff --git a/ccmain/output.cpp b/ccmain/output.cpp index 955ef2ccb5..ea2fa98505 100644 --- a/ccmain/output.cpp +++ b/ccmain/output.cpp @@ -29,14 +29,12 @@ #include #endif #include "helpers.h" -#include "tfacep.h" #include "tessvars.h" #include "control.h" #include "secname.h" #include "reject.h" #include "docqual.h" #include "output.h" -#include "bestfirst.h" #include "globals.h" #include "tesseractclass.h" @@ -242,13 +240,7 @@ void Tesseract::write_results(PAGE_RES_IT &page_res_it, (word->best_choice->unichar_id(0) == space)) { /* Prevent adjacent tilde across words - we know that adjacent tildes within words have been removed */ - word->best_choice->remove_unichar_id(0); - if (word->best_choice->blob_choices() != NULL) { - BLOB_CHOICE_LIST_C_IT blob_choices_it(word->best_choice->blob_choices()); - if (!blob_choices_it.empty()) delete blob_choices_it.extract(); - } - word->reject_map.remove_pos (0); - word->box_word->DeleteBox(0); + word->MergeAdjacentBlobs(0); } if (newline_type || (word->word->flag (W_REP_CHAR) && tessedit_write_rep_codes)) diff --git a/ccmain/pageiterator.cpp b/ccmain/pageiterator.cpp index 81cd5c0477..b7bed56cd5 100644 --- a/ccmain/pageiterator.cpp +++ b/ccmain/pageiterator.cpp @@ -303,16 +303,22 @@ bool PageIterator::BoundingBoxInternal(PageIteratorLevel level, bool PageIterator::BoundingBox(PageIteratorLevel level, int* left, int* top, int* right, int* bottom) const { + return BoundingBox(level, 0, left, top, right, bottom); +} + +bool PageIterator::BoundingBox(PageIteratorLevel level, const int padding, + int* left, int* top, + int* right, int* bottom) const { if (!BoundingBoxInternal(level, left, top, right, bottom)) return false; // Convert to the coordinate system of the original image. - *left = ClipToRange(*left / scale_ + rect_left_, + *left = ClipToRange(*left / scale_ + rect_left_ - padding, rect_left_, rect_left_ + rect_width_); - *top = ClipToRange(*top / scale_ + rect_top_, + *top = ClipToRange(*top / scale_ + rect_top_ - padding, rect_top_, rect_top_ + rect_height_); - *right = ClipToRange((*right + scale_ - 1) / scale_ + rect_left_, + *right = ClipToRange((*right + scale_ - 1) / scale_ + rect_left_ + padding, *left, rect_left_ + rect_width_); - *bottom = ClipToRange((*bottom + scale_ - 1) / scale_ + rect_top_, + *bottom = ClipToRange((*bottom + scale_ - 1) / scale_ + rect_top_ + padding, *top, rect_top_ + rect_height_); return true; } @@ -546,14 +552,15 @@ void PageIterator::BeginWord(int offset) { // Recognition has been done, so we are using the box_word, which // is already baseline denormalized. word_length_ = word_res->best_choice->length(); - ASSERT_HOST(word_res->box_word != NULL); - if (word_res->box_word->length() != word_length_) { - tprintf("Corrupted word! best_choice[len=%d] = %s, box_word[len=%d]: ", - word_length_, word_res->best_choice->unichar_string().string(), - word_res->box_word->length()); - word_res->box_word->bounding_box().print(); + if (word_res->box_word != NULL) { + if (word_res->box_word->length() != word_length_) { + tprintf("Corrupted word! best_choice[len=%d] = %s, box_word[len=%d]: ", + word_length_, word_res->best_choice->unichar_string().string(), + word_res->box_word->length()); + word_res->box_word->bounding_box().print(); + } + ASSERT_HOST(word_res->box_word->length() == word_length_); } - ASSERT_HOST(word_res->box_word->length() == word_length_); word_ = NULL; // We will be iterating the box_word. if (cblob_it_ != NULL) { @@ -574,4 +581,13 @@ void PageIterator::BeginWord(int offset) { } } +bool PageIterator::SetWordBlamerBundle(BlamerBundle *blamer_bundle) { + if (it_->word() != NULL) { + it_->word()->blamer_bundle = blamer_bundle; + return true; + } else { + return false; + } +} + } // namespace tesseract. diff --git a/ccmain/pageiterator.h b/ccmain/pageiterator.h index 0d6084f94c..d3dfc78fb0 100644 --- a/ccmain/pageiterator.h +++ b/ccmain/pageiterator.h @@ -24,6 +24,7 @@ #include "publictypes.h" #include "platform.h" +class BlamerBundle; class C_BLOB_IT; class PBLOB_IT; class PAGE_RES; @@ -189,6 +190,8 @@ class TESS_API PageIterator { */ bool BoundingBox(PageIteratorLevel level, int* left, int* top, int* right, int* bottom) const; + bool BoundingBox(PageIteratorLevel level, const int padding, + int* left, int* top, int* right, int* bottom) const; /** * Returns the bounding rectangle of the object in a coordinate system of the * working image rectangle having its origin at (rect_left_, rect_top_) with @@ -282,6 +285,12 @@ class TESS_API PageIterator { bool *is_crown, int *first_line_indent) const; + // If the current WERD_RES (it_->word()) is not NULL, sets the BlamerBundle + // of the current word to the given pointer (takes ownership of the pointer) + // and returns true. + // Can only be used when iterating on the word level. + bool SetWordBlamerBundle(BlamerBundle *blamer_bundle); + protected: /** * Sets up the internal data for iterating the blobs of a new word, then diff --git a/ccmain/paragraphs.cpp b/ccmain/paragraphs.cpp index a6d1d306ad..78b8f6ead8 100644 --- a/ccmain/paragraphs.cpp +++ b/ccmain/paragraphs.cpp @@ -16,8 +16,8 @@ ** limitations under the License. * **********************************************************************/ -#ifdef _MSC_VER -#define __func__ __FUNCTION__ +#ifdef _MSC_VER +#define __func__ __FUNCTION__ #endif #include @@ -40,11 +40,6 @@ namespace tesseract { -// The tab vectors for a given line should be ignored if both its tab vectors -// are infrequent, specifically, if both tab vectors appear at most once per -// kStrayLinePer lines in a block. -const int kStrayLinePer = 6; - // Special "weak" ParagraphModels. const ParagraphModel *kCrownLeft = reinterpret_cast(0xDEAD111F); @@ -727,7 +722,15 @@ void CalculateTabStops(GenericVector *rows, // tab stop is frequent. SimpleClusterer lefts(tolerance); SimpleClusterer rights(tolerance); - int infrequent_enough_to_ignore = (row_end - row_start) / kStrayLinePer; + + // Outlier elimination. We might want to switch this to test outlier-ness + // based on how strange a position an outlier is in instead of or in addition + // to how rare it is. These outliers get re-added if we end up having too + // few tab stops, to work with, however. + int infrequent_enough_to_ignore = 0; + if (row_end - row_start >= 8) infrequent_enough_to_ignore = 1; + if (row_end - row_start >= 20) infrequent_enough_to_ignore = 2; + for (int i = row_start; i < row_end; i++) { int lidx = ClosestCluster(initial_left_tabs, (*rows)[i].lindent_); int ridx = ClosestCluster(initial_right_tabs, (*rows)[i].rindent_); @@ -739,6 +742,54 @@ void CalculateTabStops(GenericVector *rows, } lefts.GetClusters(left_tabs); rights.GetClusters(right_tabs); + + if ((left_tabs->size() == 1 && right_tabs->size() >= 4) || + (right_tabs->size() == 1 && left_tabs->size() >= 4)) { + // One side is really ragged, and the other only has one tab stop, + // so those "insignificant outliers" are probably important, actually. + // This often happens on a page of an index. Add back in the ones + // we omitted in the first pass. + for (int i = row_start; i < row_end; i++) { + int lidx = ClosestCluster(initial_left_tabs, (*rows)[i].lindent_); + int ridx = ClosestCluster(initial_right_tabs, (*rows)[i].rindent_); + if (!(initial_left_tabs[lidx].count > infrequent_enough_to_ignore || + initial_right_tabs[ridx].count > infrequent_enough_to_ignore)) { + lefts.Add((*rows)[i].lindent_); + rights.Add((*rows)[i].rindent_); + } + } + } + lefts.GetClusters(left_tabs); + rights.GetClusters(right_tabs); + + // If one side is almost a two-indent aligned side, and the other clearly + // isn't, try to prune out the least frequent tab stop from that side. + if (left_tabs->size() == 3 && right_tabs->size() >= 4) { + int to_prune = -1; + for (int i = left_tabs->size() - 1; i >= 0; i--) { + if (to_prune < 0 || + (*left_tabs)[i].count < (*left_tabs)[to_prune].count) { + to_prune = i; + } + } + if (to_prune >= 0 && + (*left_tabs)[to_prune].count <= infrequent_enough_to_ignore) { + left_tabs->remove(to_prune); + } + } + if (right_tabs->size() == 3 && right_tabs->size() >= 4) { + int to_prune = -1; + for (int i = right_tabs->size() - 1; i >= 0; i--) { + if (to_prune < 0 || + (*right_tabs)[i].count < (*right_tabs)[to_prune].count) { + to_prune = i; + } + } + if (to_prune >= 0 && + (*right_tabs)[to_prune].count <= infrequent_enough_to_ignore) { + right_tabs->remove(to_prune); + } + } } // Given a paragraph model mark rows[row_start, row_end) as said model @@ -816,6 +867,11 @@ struct GeometricClassifierState { tolerance = InterwordSpace(*r, r_start, r_end); CalculateTabStops(r, r_start, r_end, tolerance, &left_tabs, &right_tabs); + if (debug_level >= 3) { + tprintf("Geometry: TabStop cluster tolerance = %d; " + "%d left tabs; %d right tabs\n", + tolerance, left_tabs.size(), right_tabs.size()); + } ltr = (*r)[r_start].ri_->ltr; } @@ -1079,16 +1135,18 @@ void GeometricClassify(int debug_level, firsts[s.AlignsideTabIndex(s.row_start)]++; // For each line, if the first word would have fit on the previous // line count it as a likely paragraph start line. + bool jam_packed = true; for (int i = s.row_start + 1; i < s.row_end; i++) { if (s.FirstWordWouldHaveFit(i - 1, i)) { firsts[s.AlignsideTabIndex(i)]++; + jam_packed = false; } } // Make an extra accounting for the last line of the paragraph just // in case it's the only short line in the block. That is, take its // first word as typical and see if this looks like the *last* line // of a paragraph. If so, mark the *other* indent as probably a first. - if (s.FirstWordWouldHaveFit(s.row_end - 1, s.row_end - 1)) { + if (jam_packed && s.FirstWordWouldHaveFit(s.row_end - 1, s.row_end - 1)) { firsts[1 - s.AlignsideTabIndex(s.row_end - 1)]++; } @@ -1543,24 +1601,26 @@ void RecomputeMarginsAndClearHypotheses( } } -// Return the minimum inter-word space in rows[row_start, row_end). +// Return the median inter-word space in rows[row_start, row_end). int InterwordSpace(const GenericVector &rows, int row_start, int row_end) { if (row_end < row_start + 1) return 1; - bool legit = false; - int natural_space = rows[row_start].ri_->average_interword_space; + int word_height = (rows[row_start].ri_->lword_box.height() + + rows[row_end - 1].ri_->lword_box.height()) / 2; + int word_width = (rows[row_start].ri_->lword_box.width() + + rows[row_end - 1].ri_->lword_box.width()) / 2; + STATS spacing_widths(0, 5 + word_width); for (int i = row_start; i < row_end; i++) { if (rows[i].ri_->num_words > 1) { - if (!legit) { - natural_space = rows[i].ri_->average_interword_space; - legit = true; - } else { - if (rows[i].ri_->average_interword_space < natural_space) - natural_space = rows[i].ri_->average_interword_space; - } + spacing_widths.add(rows[i].ri_->average_interword_space, 1); } } - return natural_space; + int minimum_reasonable_space = word_height / 3; + if (minimum_reasonable_space < 2) + minimum_reasonable_space = 2; + int median = spacing_widths.median(); + return (median > minimum_reasonable_space) + ? median : minimum_reasonable_space; } // Return whether the first word on the after line can fit in the space at @@ -2274,6 +2334,7 @@ void DetectParagraphs(int debug_level, GeometricClassify(debug_level, &rows, leftovers[i].begin, leftovers[i].end, &theory); } + // Undo any flush models for which there's little evidence. DowngradeWeakestToCrowns(debug_level, &theory, &rows); diff --git a/ccmain/recogtraining.cpp b/ccmain/recogtraining.cpp index 5047ff6629..d9c27b9bed 100644 --- a/ccmain/recogtraining.cpp +++ b/ccmain/recogtraining.cpp @@ -23,7 +23,6 @@ #include "control.h" #include "cutil.h" #include "host.h" -#include "permute.h" #include "ratngs.h" #include "reject.h" #include "stopper.h" @@ -38,10 +37,6 @@ FILE *Tesseract::init_recog_training(const STRING &fname) { if (tessedit_ambigs_training) { tessedit_tess_adaption_mode.set_value(0); // turn off adaption tessedit_enable_doc_dict.set_value(0); // turn off document dictionary - save_blob_choices.set_value(1); // save individual char choices - getDict().save_raw_choices.set_value(1); // save raw choices - getDict().permute_only_top.set_value(true); // use only top choice permuter - tessedit_ok_mode.set_value(0); // turn off context checking // Explore all segmentations. getDict().stopper_no_acceptable_choices.set_value(1); } @@ -156,6 +151,47 @@ void Tesseract::recog_training_segmented(const STRING &fname, examined_words, total_words); } +// Helper prints the given set of blob choices. +static void PrintPath(int length, const BLOB_CHOICE** blob_choices, + const UNICHARSET& unicharset, + const char *label, FILE *output_file) { + float rating = 0.0f; + float certainty = 0.0f; + for (int i = 0; i < length; ++i) { + const BLOB_CHOICE* blob_choice = blob_choices[i]; + fprintf(output_file, "%s", + unicharset.id_to_unichar(blob_choice->unichar_id())); + rating += blob_choice->rating(); + if (certainty > blob_choice->certainty()) + certainty = blob_choice->certainty(); + } + fprintf(output_file, "\t%s\t%.4f\t%.4f\n", + label, rating, certainty); +} + +// Helper recursively prints all paths through the ratings matrix, starting +// at column col. +static void PrintMatrixPaths(int col, int dim, + const MATRIX& ratings, + int length, const BLOB_CHOICE** blob_choices, + const UNICHARSET& unicharset, + const char *label, FILE *output_file) { + for (int row = col; row < dim && row - col < ratings.bandwidth(); ++row) { + if (ratings.get(col, row) != NOT_CLASSIFIED) { + BLOB_CHOICE_IT bc_it(ratings.get(col, row)); + for (bc_it.mark_cycle_pt(); !bc_it.cycled_list(); bc_it.forward()) { + blob_choices[length] = bc_it.data(); + if (row + 1 < dim) { + PrintMatrixPaths(row + 1, dim, ratings, length + 1, blob_choices, + unicharset, label, output_file); + } else { + PrintPath(length + 1, blob_choices, unicharset, label, output_file); + } + } + } + } +} + // Runs classify_word_pass1() on the current word. Outputs Tesseract's // raw choice as a result of the classification. For words labeled with a // single unichar also outputs all alternatives from blob_choices of the @@ -165,44 +201,25 @@ void Tesseract::ambigs_classify_and_output(WERD_RES *werd_res, BLOCK_RES *block_res, const char *label, FILE *output_file) { - int offset; // Classify word. fflush(stdout); classify_word_pass1(block_res->block, row_res->row, werd_res); WERD_CHOICE *best_choice = werd_res->best_choice; ASSERT_HOST(best_choice != NULL); - ASSERT_HOST(best_choice->blob_choices() != NULL); // Compute the number of unichars in the label. - int label_num_unichars = 0; - int step = 1; // should be non-zero on the first iteration - for (offset = 0; label[offset] != '\0' && step > 0; - step = werd_res->uch_set->step(label + offset), - offset += step, ++label_num_unichars); - if (step == 0) { + GenericVector encoding; + if (!unicharset.encode_string(label, true, &encoding, NULL, NULL)) { tprintf("Not outputting illegal unichar %s\n", label); return; } - // Output all classifier choices for the unigrams (1->1 classifications). - if (label_num_unichars == 1 && best_choice->blob_choices()->length() == 1) { - BLOB_CHOICE_LIST_C_IT outer_blob_choice_it; - outer_blob_choice_it.set_to_list(best_choice->blob_choices()); - BLOB_CHOICE_IT blob_choice_it; - blob_choice_it.set_to_list(outer_blob_choice_it.data()); - for (blob_choice_it.mark_cycle_pt(); - !blob_choice_it.cycled_list(); - blob_choice_it.forward()) { - BLOB_CHOICE *blob_choice = blob_choice_it.data(); - if (blob_choice->unichar_id() != INVALID_UNICHAR_ID) { - fprintf(output_file, "%s\t%s\t%.4f\t%.4f\n", - unicharset.id_to_unichar(blob_choice->unichar_id()), - label, blob_choice->rating(), blob_choice->certainty()); - } - } - } - // Output raw choices for many->many and 1->many classifications. - getDict().PrintAmbigAlternatives(output_file, label, label_num_unichars); + // Dump all paths through the ratings matrix (which is normally small). + int dim = werd_res->ratings->dimension(); + const BLOB_CHOICE** blob_choices = new const BLOB_CHOICE*[dim]; + PrintMatrixPaths(0, dim, *werd_res->ratings, 0, blob_choices, + unicharset, label, output_file); + delete [] blob_choices; } } // namespace tesseract diff --git a/ccmain/reject.cpp b/ccmain/reject.cpp index a3f6377f1f..bb337d089a 100644 --- a/ccmain/reject.cpp +++ b/ccmain/reject.cpp @@ -30,13 +30,13 @@ #include "scanutils.h" #include #include -#include "memry.h" +#include "genericvector.h" #include "reject.h" -#include "tfacep.h" #include "imgs.h" #include "control.h" #include "docqual.h" #include "secname.h" +#include "globaloc.h" // For err_exit. #include "globals.h" #include "helpers.h" @@ -58,126 +58,26 @@ CLISTIZEH (STRING) CLISTIZE (STRING) *************************************************************************/ namespace tesseract { -void Tesseract::set_done( //set done flag - WERD_RES *word, - inT16 pass) { - /* - 0: Original heuristic used in Tesseract and Ray's prototype Resaljet - */ - if (tessedit_ok_mode == 0) { - /* NOTE - done even if word contains some or all spaces !!! */ - word->done = word->tess_accepted; - } - /* - 1: Reject words containing blanks and on pass 1 reject I/l/1 conflicts - */ - else if (tessedit_ok_mode == 1) { - word->done = word->tess_accepted && - (strchr (word->best_choice->unichar_string().string (), ' ') == NULL); - - if (word->done && (pass == 1) && one_ell_conflict (word, FALSE)) - word->done = FALSE; - } - /* - 2: as 1 + only accept dict words or numerics in pass 1 - */ - else if (tessedit_ok_mode == 2) { - word->done = word->tess_accepted && - (strchr (word->best_choice->unichar_string().string (), ' ') == NULL); - - if (word->done && (pass == 1) && one_ell_conflict (word, FALSE)) - word->done = FALSE; - - if (word->done && - (pass == 1) && - (word->best_choice->permuter () != SYSTEM_DAWG_PERM) && - (word->best_choice->permuter () != FREQ_DAWG_PERM) && - (word->best_choice->permuter () != USER_DAWG_PERM) && - (word->best_choice->permuter () != NUMBER_PERM)) { - #ifndef SECURE_NAMES - if (tessedit_rejection_debug) - tprintf ("\nVETO Tess accepting poor word \"%s\"\n", - word->best_choice->unichar_string().string ()); - #endif - word->done = FALSE; - } - } - /* - 3: as 2 + only accept dict words or numerics in pass 2 as well - */ - else if (tessedit_ok_mode == 3) { - word->done = word->tess_accepted && - (strchr (word->best_choice->unichar_string().string (), ' ') == NULL); - - if (word->done && (pass == 1) && one_ell_conflict (word, FALSE)) - word->done = FALSE; - - if (word->done && - (word->best_choice->permuter () != SYSTEM_DAWG_PERM) && - (word->best_choice->permuter () != FREQ_DAWG_PERM) && - (word->best_choice->permuter () != USER_DAWG_PERM) && - (word->best_choice->permuter () != NUMBER_PERM)) { - #ifndef SECURE_NAMES - if (tessedit_rejection_debug) - tprintf ("\nVETO Tess accepting poor word \"%s\"\n", - word->best_choice->unichar_string().string ()); - #endif - word->done = FALSE; - } - } - /* - 4: as 2 + reject dict ambigs in pass 1 - */ - else if (tessedit_ok_mode == 4) { - word->done = word->tess_accepted && - (strchr (word->best_choice->unichar_string().string (), ' ') == NULL); - - if (word->done && (pass == 1) && one_ell_conflict (word, FALSE)) +void Tesseract::set_done(WERD_RES *word, inT16 pass) { + word->done = word->tess_accepted && + (strchr(word->best_choice->unichar_string().string(), ' ') == NULL); + bool word_is_ambig = word->best_choice->dangerous_ambig_found(); + bool word_from_dict = word->best_choice->permuter() == SYSTEM_DAWG_PERM || + word->best_choice->permuter() == FREQ_DAWG_PERM || + word->best_choice->permuter() == USER_DAWG_PERM; + if (word->done && (pass == 1) && (!word_from_dict || word_is_ambig) && + one_ell_conflict(word, FALSE)) { + if (tessedit_rejection_debug) tprintf("one_ell_conflict detected\n"); + word->done = FALSE; + } + if (word->done && ((!word_from_dict && + word->best_choice->permuter() != NUMBER_PERM) || word_is_ambig)) { + if (tessedit_rejection_debug) tprintf("non-dict or ambig word detected\n"); word->done = FALSE; - - if (word->done && - (pass == 1) && - (((word->best_choice->permuter () != SYSTEM_DAWG_PERM) && - (word->best_choice->permuter () != FREQ_DAWG_PERM) && - (word->best_choice->permuter () != USER_DAWG_PERM) && - (word->best_choice->permuter () != NUMBER_PERM)) || - (test_ambig_word (word)))) { - #ifndef SECURE_NAMES - if (tessedit_rejection_debug) - tprintf ("\nVETO Tess accepting poor word \"%s\"\n", - word->best_choice->unichar_string().string ()); - #endif - word->done = FALSE; - } - } - /* - 5: as 3 + reject dict ambigs in both passes - */ - else if (tessedit_ok_mode == 5) { - word->done = word->tess_accepted && - (strchr (word->best_choice->unichar_string().string (), ' ') == NULL); - - if (word->done && (pass == 1) && one_ell_conflict (word, FALSE)) - word->done = FALSE; - - if (word->done && - (((word->best_choice->permuter () != SYSTEM_DAWG_PERM) && - (word->best_choice->permuter () != FREQ_DAWG_PERM) && - (word->best_choice->permuter () != USER_DAWG_PERM) && - (word->best_choice->permuter () != NUMBER_PERM)) || - (test_ambig_word (word)))) { - #ifndef SECURE_NAMES - if (tessedit_rejection_debug) - tprintf ("\nVETO Tess accepting poor word \"%s\"\n", - word->best_choice->unichar_string().string ()); - #endif - word->done = FALSE; - } } - - else { - tprintf ("BAD tessedit_ok_mode\n"); - err_exit(); + if (tessedit_rejection_debug) { + tprintf("set_done(): done=%d\n", word->done); + word->best_choice->print(""); } } @@ -189,12 +89,7 @@ void Tesseract::set_done( //set done flag * * Sets a reject map for the word. *************************************************************************/ -void Tesseract::make_reject_map( //make rej map for wd //detailed results - WERD_RES *word, - BLOB_CHOICE_LIST_CLIST *blob_choices, - ROW *row, - inT16 pass //1st or 2nd? - ) { +void Tesseract::make_reject_map(WERD_RES *word, ROW *row, inT16 pass) { int i; int offset; @@ -208,7 +103,7 @@ void Tesseract::make_reject_map( //make rej map for wd //detailed results */ if (tessedit_reject_mode == 0) { if (!word->done) - reject_poor_matches(word, blob_choices); + reject_poor_matches(word); } else if (tessedit_reject_mode == 5) { /* 5: Reject I/1/l from words where there is no strong contextual confirmation; @@ -313,45 +208,13 @@ void Tesseract::reject_I_1_L(WERD_RES *word) { } // namespace tesseract -void reject_poor_matches( //detailed results - WERD_RES *word, - BLOB_CHOICE_LIST_CLIST *blob_choices) { - float threshold; - inT16 i = 0; - inT16 offset = 0; - //super iterator - BLOB_CHOICE_LIST_C_IT list_it = blob_choices; - BLOB_CHOICE_IT choice_it; //real iterator - - #ifndef SECURE_NAMES - if (strlen(word->best_choice->unichar_lengths().string()) != - list_it.length()) { - tprintf - ("ASSERT FAIL string:\"%s\"; strlen=%d; choices len=%d; blob len=%d\n", - word->best_choice->unichar_string().string(), - strlen (word->best_choice->unichar_lengths().string()), list_it.length(), - word->box_word->length()); - } - #endif - ASSERT_HOST (strlen (word->best_choice->unichar_lengths().string ()) == - list_it.length ()); - ASSERT_HOST(word->box_word->length() == list_it.length()); - threshold = compute_reject_threshold (blob_choices); - - for (list_it.mark_cycle_pt (); - !list_it.cycled_list (); list_it.forward (), i++, - offset += word->best_choice->unichar_lengths()[i]) { - /* NB - only compares the threshold against the TOP choice char in the - choices list for a blob !! - the selected one may be below the threshold - */ - choice_it.set_to_list (list_it.data ()); - if ((word->best_choice->unichar_string()[offset] == ' ') || - (choice_it.length () == 0)) - //rej unrecognised blobs - word->reject_map[i].setrej_tess_failure (); - else if (choice_it.data ()->certainty () < threshold) - //rej poor score blob - word->reject_map[i].setrej_poor_match (); +void reject_poor_matches(WERD_RES *word) { + float threshold = compute_reject_threshold(word->best_choice); + for (int i = 0; i < word->best_choice->length(); ++i) { + if (word->best_choice->unichar_id(i) == UNICHAR_SPACE) + word->reject_map[i].setrej_tess_failure(); + else if (word->best_choice->certainty(i) < threshold) + word->reject_map[i].setrej_poor_match(); } } @@ -364,52 +227,32 @@ void reject_poor_matches( //detailed results * gap in the certainty value. **********************************************************************/ -float compute_reject_threshold( //compute threshold //detailed results - BLOB_CHOICE_LIST_CLIST *blob_choices) { - inT16 index; //to ratings - inT16 blob_count; //no of blobs in word - inT16 ok_blob_count = 0; //non TESS rej blobs in word - float *ratings; //array of confidences - float threshold; //rejection threshold - float bestgap; //biggest gap - float gapstart; //bottom of gap - //super iterator - BLOB_CHOICE_LIST_C_IT list_it = blob_choices; - BLOB_CHOICE_IT choice_it; //real iterator - - blob_count = blob_choices->length (); - ratings = (float *) alloc_mem (blob_count * sizeof (float)); - for (list_it.mark_cycle_pt (), index = 0; - !list_it.cycled_list (); list_it.forward (), index++) { - choice_it.set_to_list (list_it.data ()); - if (choice_it.length () > 0) { - ratings[ok_blob_count] = choice_it.data ()->certainty (); - //get in an array - // tprintf("Rating[%d]=%c %g %g\n", - // index,choice_it.data()->char_class(), - // choice_it.data()->rating(),choice_it.data()->certainty()); - ok_blob_count++; - } - } - ASSERT_HOST (index == blob_count); - qsort (ratings, ok_blob_count, sizeof (float), sort_floats); - //sort them - bestgap = 0; - gapstart = ratings[0] - 1; //all reject if none better - if (ok_blob_count >= 3) { - for (index = 0; index < ok_blob_count - 1; index++) { +float compute_reject_threshold(WERD_CHOICE* word) { + float threshold; // rejection threshold + float bestgap = 0.0f; // biggest gap + float gapstart; // bottom of gap + // super iterator + BLOB_CHOICE_IT choice_it; // real iterator + + int blob_count = word->length(); + GenericVector ratings; + ratings.init_to_size(blob_count, 0.0f); + for (int i = 0; i < blob_count; ++i) { + ratings[i] = word->certainty(i); + } + ratings.sort(); + gapstart = ratings[0] - 1; // all reject if none better + if (blob_count >= 3) { + for (int index = 0; index < blob_count - 1; index++) { if (ratings[index + 1] - ratings[index] > bestgap) { bestgap = ratings[index + 1] - ratings[index]; - //find biggest + // find biggest gapstart = ratings[index]; } } } threshold = gapstart + bestgap / 2; - // tprintf("First=%g, last=%g, gap=%g, threshold=%g\n", - // ratings[0],ratings[index],bestgap,threshold); - free_mem(ratings); return threshold; } @@ -680,21 +523,6 @@ BOOL8 Tesseract::word_contains_non_1_digit(const char *word, return FALSE; } - -BOOL8 Tesseract::test_ambig_word( //test for ambiguity - WERD_RES *word) { - BOOL8 ambig = FALSE; - - if ((word->best_choice->permuter () == SYSTEM_DAWG_PERM) || - (word->best_choice->permuter () == FREQ_DAWG_PERM) || - (word->best_choice->permuter () == USER_DAWG_PERM)) { - ambig = !getDict().NoDangerousAmbig( - word->best_choice, NULL, false, NULL, NULL); - } - return ambig; -} - - /************************************************************************* * dont_allow_1Il() * Dont unreject LONE accepted 1Il conflict set chars @@ -786,10 +614,9 @@ inT16 Tesseract::safe_dict_word(const WERD_RES *werd_res) { return dict_word_type == DOC_DAWG_PERM ? 0 : dict_word_type; } -// Note: After running this function word_res->best_choice->blob_choices() -// might not contain the right BLOB_CHOICE coresponding to each character -// in word_res->best_choice. However, the length of blob_choices and -// word_res->best_choice will remain the same. +// Note: After running this function word_res->ratings +// might not contain the right BLOB_CHOICE corresponding to each character +// in word_res->best_choice. void Tesseract::flip_hyphens(WERD_RES *word_res) { WERD_CHOICE *best_choice = word_res->best_choice; int i; @@ -801,16 +628,16 @@ void Tesseract::flip_hyphens(WERD_RES *word_res) { if (tessedit_lower_flip_hyphen <= 1) return; - TBLOB* blob = word_res->rebuild_word->blobs; + int num_blobs = word_res->rebuild_word->NumBlobs(); UNICHAR_ID unichar_dash = word_res->uch_set->unichar_to_id("-"); bool modified = false; - for (i = 0; i < best_choice->length() && blob != NULL; ++i, - blob = blob->next) { + for (i = 0; i < best_choice->length() && i < num_blobs; ++i) { + TBLOB* blob = word_res->rebuild_word->blobs[i]; out_box = blob->bounding_box(); - if (blob->next == NULL) + if (i + 1 == num_blobs) next_left = 9999; else - next_left = blob->next->bounding_box().left(); + next_left = word_res->rebuild_word->blobs[i + 1]->bounding_box().left(); // Dont touch small or touching blobs - it is too dangerous. if ((out_box.width() > 8 * word_res->denorm.x_scale()) && (out_box.left() > prev_right) && (out_box.right() < next_left)) { @@ -846,10 +673,9 @@ void Tesseract::flip_hyphens(WERD_RES *word_res) { } } -// Note: After running this function word_res->best_choice->blob_choices() -// might not contain the right BLOB_CHOICE coresponding to each character -// in word_res->best_choice. However, the length of blob_choices and -// word_res->best_choice will remain the same. +// Note: After running this function word_res->ratings +// might not contain the right BLOB_CHOICE corresponding to each character +// in word_res->best_choice. void Tesseract::flip_0O(WERD_RES *word_res) { WERD_CHOICE *best_choice = word_res->best_choice; int i; @@ -858,9 +684,9 @@ void Tesseract::flip_0O(WERD_RES *word_res) { if (!tessedit_flip_0O) return; - TBLOB* blob = word_res->rebuild_word->blobs; - for (i = 0; i < best_choice->length() && blob != NULL; ++i, - blob = blob->next) { + int num_blobs = word_res->rebuild_word->NumBlobs(); + for (i = 0; i < best_choice->length() && i < num_blobs; ++i) { + TBLOB* blob = word_res->rebuild_word->blobs[i]; if (word_res->uch_set->get_isupper(best_choice->unichar_id(i)) || word_res->uch_set->get_isdigit(best_choice->unichar_id(i))) { out_box = blob->bounding_box(); diff --git a/ccmain/reject.h b/ccmain/reject.h index e8175f5a1e..cffb262cbe 100644 --- a/ccmain/reject.h +++ b/ccmain/reject.h @@ -24,8 +24,8 @@ #include "pageres.h" void reject_blanks(WERD_RES *word); -void reject_poor_matches(WERD_RES *word, BLOB_CHOICE_LIST_CLIST *blob_choices); -float compute_reject_threshold(BLOB_CHOICE_LIST_CLIST *blob_choices); +void reject_poor_matches(WERD_RES *word); +float compute_reject_threshold(WERD_CHOICE* word); BOOL8 word_contains_non_1_digit(const char *word, const char *word_lengths); void dont_allow_1Il(WERD_RES *word); void flip_hyphens(WERD_RES *word); diff --git a/ccmain/resultiterator.h b/ccmain/resultiterator.h index c9d880b41c..cde19f0825 100644 --- a/ccmain/resultiterator.h +++ b/ccmain/resultiterator.h @@ -24,8 +24,9 @@ #include "platform.h" #include "ltrresultiterator.h" -#include "genericvector.h" +template class GenericVector; +template class GenericVectorEqEq; class BLOB_CHOICE_IT; class WERD_RES; class STRING; diff --git a/ccmain/scaleimg.cpp b/ccmain/scaleimg.cpp index a3e53058a5..6848327b9f 100644 --- a/ccmain/scaleimg.cpp +++ b/ccmain/scaleimg.cpp @@ -31,6 +31,7 @@ #include #include #include "fileerr.h" +#include "globaloc.h" // For err_exit. #include "tprintf.h" #include "img.h" #include "imgscale.h" diff --git a/ccmain/tessbox.cpp b/ccmain/tessbox.cpp index fff8835388..6efbd3aaed 100644 --- a/ccmain/tessbox.cpp +++ b/ccmain/tessbox.cpp @@ -21,25 +21,22 @@ #pragma warning(disable:4244) // Conversion warnings #endif -#include "tfacep.h" -#include "tfacepp.h" -#include "tessbox.h" #include "mfoutline.h" +#include "tessbox.h" #include "tesseractclass.h" #define EXTERN /** - * @name tess_segment_pass1 + * @name tess_segment_pass_n * - * Segment a word using the pass1 conditions of the tess segmenter. + * Segment a word using the pass_n conditions of the tess segmenter. + * @param pass_n pass number * @param word word to do - * @param blob_choices list of blob lists */ namespace tesseract { -void Tesseract::tess_segment_pass1(WERD_RES *word, - BLOB_CHOICE_LIST_CLIST *blob_choices) { +void Tesseract::tess_segment_pass_n(int pass_n, WERD_RES *word) { int saved_enable_assoc = 0; int saved_chop_enable = 0; @@ -48,46 +45,17 @@ void Tesseract::tess_segment_pass1(WERD_RES *word, saved_chop_enable = chop_enable; wordrec_enable_assoc.set_value(0); chop_enable.set_value(0); - if (word->word->flag(W_REP_CHAR)) - getDict().permute_only_top.set_value(true); } - set_pass1(); - recog_word(word, blob_choices); + if (pass_n == 1) + set_pass1(); + else + set_pass2(); + recog_word(word); + if (word->best_choice == NULL) + word->SetupFake(*word->uch_set); if (word->word->flag(W_DONT_CHOP)) { wordrec_enable_assoc.set_value(saved_enable_assoc); chop_enable.set_value(saved_chop_enable); - getDict().permute_only_top.set_value(false); - } -} - - -/** - * @name tess_segment_pass2 - * - * Segment a word using the pass2 conditions of the tess segmenter. - * @param word word to do - * @param blob_choices list of blob lists - */ - -void Tesseract::tess_segment_pass2(WERD_RES *word, - BLOB_CHOICE_LIST_CLIST *blob_choices) { - int saved_enable_assoc = 0; - int saved_chop_enable = 0; - - if (word->word->flag(W_DONT_CHOP)) { - saved_enable_assoc = wordrec_enable_assoc; - saved_chop_enable = chop_enable; - wordrec_enable_assoc.set_value(0); - chop_enable.set_value(0); - if (word->word->flag(W_REP_CHAR)) - getDict().permute_only_top.set_value(true); - } - set_pass2(); - recog_word(word, blob_choices); - if (word->word->flag(W_DONT_CHOP)) { - wordrec_enable_assoc.set_value(saved_enable_assoc); - chop_enable.set_value(saved_chop_enable); - getDict().permute_only_top.set_value(false); } } @@ -98,10 +66,8 @@ void Tesseract::tess_segment_pass2(WERD_RES *word, * @param word_choice after context * @param raw_choice before context */ -BOOL8 Tesseract::tess_acceptable_word( - WERD_CHOICE *word_choice, // after context - WERD_CHOICE *raw_choice) { // before context - return getDict().AcceptableResult(*word_choice); +bool Tesseract::tess_acceptable_word(WERD_RES* word) { + return getDict().AcceptableResult(word); } diff --git a/ccmain/tessedit.cpp b/ccmain/tessedit.cpp index ece55405ef..19709f7782 100644 --- a/ccmain/tessedit.cpp +++ b/ccmain/tessedit.cpp @@ -17,30 +17,17 @@ * **********************************************************************/ -//#include -//#include -//#include -//#include -#include "tfacep.h" //must be before main.h -//#include "fileerr.h" #include "stderr.h" #include "basedir.h" #include "tessvars.h" -//#include "debgwin.h" -//#include "epapdest.h" #include "control.h" #include "imgs.h" #include "reject.h" #include "pageres.h" -//#include "gpapdest.h" #include "nwmain.h" #include "pgedit.h" #include "tprintf.h" -//#include "ipeerr.h" -//#include "restart.h" #include "tessedit.h" -//#include "fontfind.h" -#include "permute.h" #include "stopper.h" #include "intmatcher.h" #include "chop.h" @@ -190,9 +177,16 @@ bool Tesseract::init_tesseract_lang_data( if (tessdata_manager_debug_level) tprintf("Loaded unicharset\n"); right_to_left_ = unicharset.major_right_to_left(); + // Setup initial unichar ambigs table and read universal ambigs. + UNICHARSET encoder_unicharset; + encoder_unicharset.CopyFrom(unicharset); + unichar_ambigs.InitUnicharAmbigs(unicharset, use_ambigs_for_adaption); + unichar_ambigs.LoadUniversal(encoder_unicharset, &unicharset); + if (!tessedit_ambigs_training && tessdata_manager.SeekToStart(TESSDATA_AMBIGS)) { unichar_ambigs.LoadUnicharAmbigs( + encoder_unicharset, tessdata_manager.GetDataFilePtr(), tessdata_manager.GetEndOffset(TESSDATA_AMBIGS), ambigs_debug_level, use_ambigs_for_adaption, &unicharset); @@ -210,6 +204,23 @@ bool Tesseract::init_tesseract_lang_data( tprintf("Loaded Cube with combiner\n"); } + // Init ParamsModel. + // Load pass1 and pass2 weights (for now these two sets are the same, but in + // the future separate sets of weights can be generated). + for (int p = ParamsModel::PTRAIN_PASS1; + p < ParamsModel::PTRAIN_NUM_PASSES; ++p) { + language_model_->getParamsModel().SetPass( + static_cast(p)); + if (tessdata_manager.SeekToStart(TESSDATA_PARAMS_MODEL)) { + if (!language_model_->getParamsModel().LoadFromFp( + lang.string(), tessdata_manager.GetDataFilePtr(), + tessdata_manager.GetEndOffset(TESSDATA_PARAMS_MODEL))) { + return false; + } + } + } + if (tessdata_manager_debug_level) language_model_->getParamsModel().Print(); + return true; } @@ -323,6 +334,30 @@ int Tesseract::init_tesseract( tprintf("Tesseract couldn't load any languages!\n"); return -1; // Couldn't load any language! } + if (!sub_langs_.empty()) { + // In multilingual mode word ratings have to be directly comparable, + // so use the same language model weights for all languages: + // use the primary language's params model if + // tessedit_use_primary_params_model is set, + // otherwise use default language model weights. + if (tessedit_use_primary_params_model) { + for (int s = 0; s < sub_langs_.size(); ++s) { + sub_langs_[s]->language_model_->getParamsModel().Copy( + this->language_model_->getParamsModel()); + } + tprintf("Using params model of the primary language\n"); + if (tessdata_manager_debug_level) { + this->language_model_->getParamsModel().Print(); + } + } else { + this->language_model_->getParamsModel().Clear(); + for (int s = 0; s < sub_langs_.size(); ++s) { + sub_langs_[s]->language_model_->getParamsModel().Clear(); + } + tprintf("Using default language params\n"); + } + } + SetupUniversalFontIds(); return 0; } @@ -420,7 +455,7 @@ int Tesseract::init_tesseract_lm(const char *arg0, if (!init_tesseract_lang_data(arg0, textbase, language, OEM_TESSERACT_ONLY, NULL, 0, NULL, NULL, false)) return -1; - getDict().Load(); + getDict().Load(Dict::GlobalDawgCache()); tessdata_manager.End(); return 0; } diff --git a/ccmain/tesseract_cube_combiner.cpp b/ccmain/tesseract_cube_combiner.cpp index 7fd7c6b198..e17bd04c2a 100644 --- a/ccmain/tesseract_cube_combiner.cpp +++ b/ccmain/tesseract_cube_combiner.cpp @@ -221,16 +221,16 @@ bool TesseractCubeCombiner::ComputeCombinerFeatures(const string &tess_str, features->push_back(cube_best_bigram_cost); } // case-insensitive string comparison, including punctuation - int compare_nocase_punc = CompareStrings(cube_best_str.c_str(), - tess_str.c_str(), false, true); + int compare_nocase_punc = CompareStrings(cube_best_str, + tess_str, false, true); features->push_back(compare_nocase_punc == 0); // case-sensitive string comparison, ignoring punctuation - int compare_case_nopunc = CompareStrings(cube_best_str.c_str(), - tess_str.c_str(), true, false); + int compare_case_nopunc = CompareStrings(cube_best_str, + tess_str, true, false); features->push_back(compare_case_nopunc == 0); // case-insensitive string comparison, ignoring punctuation - int compare_nocase_nopunc = CompareStrings(cube_best_str.c_str(), - tess_str.c_str(), true, true); + int compare_nocase_nopunc = CompareStrings(cube_best_str, + tess_str, true, true); features->push_back(compare_nocase_nopunc == 0); return true; } diff --git a/ccmain/tfacep.h b/ccmain/tfacep.h deleted file mode 100644 index b70183d835..0000000000 --- a/ccmain/tfacep.h +++ /dev/null @@ -1,37 +0,0 @@ -/********************************************************************** - * File: tfacep.h (Formerly tfacep.h) - * Description: Declarations of C functions and C owned data. - * Author: Ray Smith - * Created: Mon Apr 27 12:51:28 BST 1992 - * - * (C) Copyright 1992, Hewlett-Packard Ltd. - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** http://www.apache.org/licenses/LICENSE-2.0 - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - * - **********************************************************************/ - -#ifndef TFACEP_H -#define TFACEP_H - -#include "host.h" -#include "blobs.h" -#include "tessarray.h" -#include "oldlist.h" -#include "permute.h" -#include "blobclass.h" -#include "stopper.h" -#include "associate.h" -#include "chop.h" -#include "structures.h" - -typedef void (*TESS_TESTER) (TBLOB *, BOOL8, char *, inT32, LIST); -typedef LIST (*TESS_MATCHER) (TBLOB *, TBLOB *, TBLOB *); - -#endif diff --git a/ccmain/tfacepp.cpp b/ccmain/tfacepp.cpp index e415453f62..3d8d99b365 100644 --- a/ccmain/tfacepp.cpp +++ b/ccmain/tfacepp.cpp @@ -25,19 +25,12 @@ #include -#ifdef __UNIX__ -#include -#endif -#include "errcode.h" -#include "ratngs.h" -#include "reject.h" -#include "werd.h" -#include "tfacep.h" -#include "tfacepp.h" -#include "tessvars.h" -#include "globals.h" -#include "reject.h" -#include "tesseractclass.h" +#include "blamer.h" +#include "errcode.h" +#include "ratngs.h" +#include "reject.h" +#include "tesseractclass.h" +#include "werd.h" #define MAX_UNDIVIDED_LENGTH 24 @@ -50,21 +43,30 @@ * Convert the output back to editor form. **********************************************************************/ namespace tesseract { -void Tesseract::recog_word(WERD_RES *word, - BLOB_CHOICE_LIST_CLIST *blob_choices) { - ASSERT_HOST(word->chopped_word->blobs != NULL); - recog_word_recursive(word, blob_choices); +void Tesseract::recog_word(WERD_RES *word) { + if (wordrec_skip_no_truth_words && (word->blamer_bundle == NULL || + word->blamer_bundle->incorrect_result_reason() == IRR_NO_TRUTH)) { + if (classify_debug_level) tprintf("No truth for word - skipping\n"); + word->tess_failed = true; + return; + } + ASSERT_HOST(!word->chopped_word->blobs.empty()); + recog_word_recursive(word); word->SetupBoxWord(); - if ((word->best_choice->length() != word->box_word->length()) || - (word->best_choice->length() != blob_choices->length())) { + if (word->best_choice->length() != word->box_word->length()) { tprintf("recog_word ASSERT FAIL String:\"%s\"; " - "Strlen=%d; #Blobs=%d; #Choices=%d\n", + "Strlen=%d; #Blobs=%d\n", word->best_choice->debug_string().string(), - word->best_choice->length(), word->box_word->length(), - blob_choices->length()); + word->best_choice->length(), word->box_word->length()); } ASSERT_HOST(word->best_choice->length() == word->box_word->length()); - ASSERT_HOST(word->best_choice->length() == blob_choices->length()); + // Check that the ratings matrix size matches the sum of all the + // segmentation states. + if (!word->StatesAllValid()) { + tprintf("Not all words have valid states relative to ratings matrix!!"); + word->DebugWordChoices(true, NULL); + ASSERT_HOST(word->StatesAllValid()); + } if (tessedit_override_permuter) { /* Override the permuter type if a straight dictionary check disagrees. */ uinT8 perm_type = word->best_choice->permuter(); @@ -105,31 +107,13 @@ void Tesseract::recog_word(WERD_RES *word, * Convert the word to tess form and pass it to the tess segmenter. * Convert the output back to editor form. **********************************************************************/ -void Tesseract::recog_word_recursive(WERD_RES *word, - BLOB_CHOICE_LIST_CLIST *blob_choices) { +void Tesseract::recog_word_recursive(WERD_RES *word) { int word_length = word->chopped_word->NumBlobs(); // no of blobs if (word_length > MAX_UNDIVIDED_LENGTH) { - return split_and_recog_word(word, blob_choices); + return split_and_recog_word(word); } - int initial_blob_choice_len = blob_choices->length(); - BLOB_CHOICE_LIST_VECTOR* tess_ratings = cc_recog(word); - - // Put BLOB_CHOICE_LISTs from tess_ratings into blob_choices. - BLOB_CHOICE_LIST_C_IT blob_choices_it(blob_choices); - for (int i = 0; i < tess_ratings->length(); ++i) { - blob_choices_it.add_to_end(tess_ratings->get(i)); - } - delete tess_ratings; - + cc_recog(word); word_length = word->rebuild_word->NumBlobs(); // No of blobs in output. - // Pad raw_choice with spaces if needed. - if (word->raw_choice->length() < word_length) { - UNICHAR_ID space_id = unicharset.unichar_to_id(" "); - while (word->raw_choice->length() < word_length) { - word->raw_choice->append_unichar_id(space_id, 1, 0.0, - word->raw_choice->certainty()); - } - } // Do sanity checks and minor fixes on best_choice. if (word->best_choice->length() > word_length) { @@ -141,21 +125,6 @@ void Tesseract::recog_word_recursive(WERD_RES *word, tprintf("Word is at:"); word->word->bounding_box().print(); } - if (blob_choices->length() - initial_blob_choice_len != word_length) { - word->best_choice->make_bad(); // force rejection - tprintf("recog_word: Choices list len:%d; blob lists len:%d\n", - blob_choices->length(), word_length); - blob_choices_it.set_to_list(blob_choices); // list of lists - while (blob_choices->length() - initial_blob_choice_len < word_length) { - blob_choices_it.add_to_end(new BLOB_CHOICE_LIST()); // add a fake one - tprintf("recog_word: Added dummy choice list\n"); - } - while (blob_choices->length() - initial_blob_choice_len > word_length) { - blob_choices_it.move_to_last(); // should never happen - delete blob_choices_it.extract(); - tprintf("recog_word: Deleted choice list\n"); - } - } if (word->best_choice->length() < word_length) { UNICHAR_ID space_id = unicharset.unichar_to_id(" "); while (word->best_choice->length() < word_length) { @@ -172,133 +141,134 @@ void Tesseract::recog_word_recursive(WERD_RES *word, * Split the word into 2 smaller pieces at the largest gap. * Recognize the pieces and stick the results back together. **********************************************************************/ - -void Tesseract::split_and_recog_word(WERD_RES *word, - BLOB_CHOICE_LIST_CLIST *blob_choices) { +void Tesseract::split_and_recog_word(WERD_RES *word) { // Find the biggest blob gap in the chopped_word. int bestgap = -MAX_INT32; - TPOINT best_split_pt; + int split_index = 0; TBLOB* best_end = NULL; TBLOB* prev_blob = NULL; - for (TBLOB* blob = word->chopped_word->blobs; blob != NULL; - blob = blob->next) { - if (prev_blob != NULL) { - TBOX prev_box = prev_blob->bounding_box(); - TBOX blob_box = blob->bounding_box(); - int gap = blob_box.left() - prev_box.right(); - if (gap > bestgap) { - bestgap = gap; - best_end = prev_blob; - best_split_pt.x = (prev_box.right() + blob_box.left()) / 2; - best_split_pt.y = (prev_box.top() + prev_box.bottom() + - blob_box.top() + blob_box.bottom()) / 4; - } + for (int b = 1; b < word->chopped_word->NumBlobs(); ++b) { + TBOX prev_box = word->chopped_word->blobs[b - 1]->bounding_box(); + TBOX blob_box = word->chopped_word->blobs[b]->bounding_box(); + int gap = blob_box.left() - prev_box.right(); + if (gap > bestgap) { + bestgap = gap; + split_index = b; } - prev_blob = blob; } - ASSERT_HOST(best_end != NULL); - ASSERT_HOST(best_end->next != NULL); + ASSERT_HOST(split_index > 0); + + WERD_RES *word2 = NULL; + BlamerBundle *orig_bb = NULL; + split_word(word, split_index, &word2, &orig_bb); + + // Recognize the first part of the word. + recog_word_recursive(word); + // Recognize the second part of the word. + recog_word_recursive(word2); + + join_words(word, word2, orig_bb); +} + + +/********************************************************************** + * split_word + * + * Split a given WERD_RES in place into two smaller words for recognition. + * split_pt is the index of the first blob to go in the second word. + * The underlying word is left alone, only the TWERD (and subsequent data) + * are split up. orig_blamer_bundle is set to the original blamer bundle, + * and will now be owned by the caller. New blamer bundles are forged for the + * two pieces. + **********************************************************************/ +void Tesseract::split_word(WERD_RES *word, + int split_pt, + WERD_RES **right_piece, + BlamerBundle **orig_blamer_bundle) const { + ASSERT_HOST(split_pt >0 && split_pt < word->chopped_word->NumBlobs()); + + // Save a copy of the blamer bundle so we can try to reconstruct it below. + BlamerBundle *orig_bb = + word->blamer_bundle ? new BlamerBundle(*word->blamer_bundle) : NULL; - // Make a copy of the word to put the 2nd half in. - WERD_RES* word2 = new WERD_RES(*word); - // Blow away the copied chopped_word, as we want to work with the blobs - // from the input chopped_word so the seam_arrays can be merged. + WERD_RES *word2 = new WERD_RES(*word); + + // blow away the copied chopped_word, as we want to work with + // the blobs from the input chopped_word so seam_arrays can be merged. + TWERD *chopped = word->chopped_word; + TWERD *chopped2 = new TWERD; + chopped2->blobs.reserve(chopped->NumBlobs() - split_pt); + for (int i = split_pt; i < chopped->NumBlobs(); ++i) { + chopped2->blobs.push_back(chopped->blobs[i]); + } + chopped->blobs.truncate(split_pt); + word->chopped_word = NULL; delete word2->chopped_word; - word2->chopped_word = new TWERD; - word2->chopped_word->blobs = best_end->next; - best_end->next = NULL; - // Make a new seamarray on both words. - free_seam_list(word->seam_array); - word->seam_array = start_seam_list(word->chopped_word->blobs); - word2->seam_array = start_seam_list(word2->chopped_word->blobs); - BlamerBundle *orig_bb = word->blamer_bundle; - STRING blamer_debug; - // Try to adjust truth information. + word2->chopped_word = NULL; + + const UNICHARSET &unicharset = *word->uch_set; + word->ClearResults(); + word2->ClearResults(); + word->chopped_word = chopped; + word2->chopped_word = chopped2; + word->SetupBasicsFromChoppedWord(unicharset); + word2->SetupBasicsFromChoppedWord(unicharset); + + // Try to adjust the blamer bundle. if (orig_bb != NULL) { - // Find truth boxes that correspond to the split in the blobs. - int b; - int begin2_truth_index = -1; - if (orig_bb->incorrect_result_reason != IRR_NO_TRUTH && - orig_bb->truth_has_char_boxes) { - int end1_x = best_end->bounding_box().right(); - int begin2_x = word2->chopped_word->blobs->bounding_box().left(); - blamer_debug = "Looking for truth split at"; - blamer_debug.add_str_int(" end1_x ", end1_x); - blamer_debug.add_str_int(" begin2_x ", begin2_x); - blamer_debug += "\nnorm_truth_word boxes:\n"; - if (orig_bb->norm_truth_word.length() > 1) { - orig_bb->norm_truth_word.BlobBox(0).append_debug(&blamer_debug); - for (b = 1; b < orig_bb->norm_truth_word.length(); ++b) { - orig_bb->norm_truth_word.BlobBox(b).append_debug(&blamer_debug); - if ((abs(end1_x - orig_bb->norm_truth_word.BlobBox(b-1).right()) < - orig_bb->norm_box_tolerance) && - (abs(begin2_x - orig_bb->norm_truth_word.BlobBox(b).left()) < - orig_bb->norm_box_tolerance)) { - begin2_truth_index = b; - blamer_debug += "Split found\n"; - break; - } - } - } - } - // Populate truth information in word and word2 with the first and second - // part of the original truth. + // TODO(rays) Looks like a leak to me. + // orig_bb should take, rather than copy. word->blamer_bundle = new BlamerBundle(); word2->blamer_bundle = new BlamerBundle(); - if (begin2_truth_index > 0) { - word->blamer_bundle->truth_has_char_boxes = true; - word->blamer_bundle->norm_box_tolerance = orig_bb->norm_box_tolerance; - word2->blamer_bundle->truth_has_char_boxes = true; - word2->blamer_bundle->norm_box_tolerance = orig_bb->norm_box_tolerance; - BlamerBundle *curr_bb = word->blamer_bundle; - for (b = 0; b < orig_bb->norm_truth_word.length(); ++b) { - if (b == begin2_truth_index) curr_bb = word2->blamer_bundle; - curr_bb->norm_truth_word.InsertBox( - b, orig_bb->norm_truth_word.BlobBox(b)); - curr_bb->truth_word.InsertBox(b, orig_bb->truth_word.BlobBox(b)); - curr_bb->truth_text.push_back(orig_bb->truth_text[b]); - } - } else if (orig_bb->incorrect_result_reason == IRR_NO_TRUTH) { - word->blamer_bundle->incorrect_result_reason = IRR_NO_TRUTH; - word2->blamer_bundle->incorrect_result_reason = IRR_NO_TRUTH; - } else { - blamer_debug += "Truth split not found"; - blamer_debug += orig_bb->truth_has_char_boxes ? - "\n" : " (no truth char boxes)\n"; - word->blamer_bundle->SetBlame(IRR_NO_TRUTH_SPLIT, blamer_debug, - NULL, wordrec_debug_blamer); - word2->blamer_bundle->SetBlame(IRR_NO_TRUTH_SPLIT, blamer_debug, - NULL, wordrec_debug_blamer); - } + orig_bb->SplitBundle(chopped->blobs.back()->bounding_box().right(), + word2->chopped_word->blobs[0]->bounding_box().left(), + wordrec_debug_blamer, + word->blamer_bundle, word2->blamer_bundle); } - // Recognize the first part of the word. - recog_word_recursive(word, blob_choices); - // Recognize the second part of the word. - recog_word_recursive(word2, blob_choices); + *right_piece = word2; + *orig_blamer_bundle = orig_bb; +} + + +/********************************************************************** + * join_words + * + * The opposite of split_word(): + * join word2 (including any recognized data / seam array / etc) + * onto the right of word and then delete word2. + * Also, if orig_bb is provided, stitch it back into word. + **********************************************************************/ +void Tesseract::join_words(WERD_RES *word, + WERD_RES *word2, + BlamerBundle *orig_bb) const { + TBOX prev_box = word->chopped_word->blobs.back()->bounding_box(); + TBOX blob_box = word2->chopped_word->blobs[0]->bounding_box(); // Tack the word2 outputs onto the end of the word outputs. - // New blobs might have appeared on the end of word1. - for (best_end = word->chopped_word->blobs; best_end->next != NULL; - best_end = best_end->next); - best_end->next = word2->chopped_word->blobs; - TBLOB* blob; - for (blob = word->rebuild_word->blobs; blob->next != NULL; blob = blob->next); - blob->next = word2->rebuild_word->blobs; - word2->chopped_word->blobs = NULL; - word2->rebuild_word->blobs = NULL; - // Copy the seams onto the end of the word1 seam_array. + word->chopped_word->blobs += word2->chopped_word->blobs; + word->rebuild_word->blobs += word2->rebuild_word->blobs; + word2->chopped_word->blobs.clear(); + word2->rebuild_word->blobs.clear(); + TPOINT split_pt; + split_pt.x = (prev_box.right() + blob_box.left()) / 2; + split_pt.y = (prev_box.top() + prev_box.bottom() + + blob_box.top() + blob_box.bottom()) / 4; + // Move the word2 seams onto the end of the word1 seam_array. // Since the seam list is one element short, an empty seam marking the // end of the last blob in the first word is needed first. - word->seam_array = add_seam(word->seam_array, - new_seam(0.0, best_split_pt, NULL, NULL, NULL)); - for (int i = 0; i < array_count(word2->seam_array); ++i) { - SEAM* seam = reinterpret_cast(array_value(word2->seam_array, i)); - array_value(word2->seam_array, i) = NULL; - word->seam_array = add_seam(word->seam_array, seam); - } + word->seam_array.push_back(new SEAM(0.0f, split_pt, NULL, NULL, NULL)); + word->seam_array += word2->seam_array; + word2->seam_array.truncate(0); + // Fix widths and gaps. + word->blob_widths += word2->blob_widths; + word->blob_gaps += word2->blob_gaps; + // Fix the ratings matrix. + int rat1 = word->ratings->dimension(); + int rat2 = word2->ratings->dimension(); + word->ratings->AttachOnCorner(word2->ratings); + ASSERT_HOST(word->ratings->dimension() == rat1 + rat2); word->best_state += word2->best_state; // Append the word choices. - *word->best_choice += *word2->best_choice; *word->raw_choice += *word2->raw_choice; // How many alt choices from each should we try to get? @@ -306,70 +276,56 @@ void Tesseract::split_and_recog_word(WERD_RES *word, // When do we start throwing away extra alt choices? const int kTooManyAltChoices = 100; - if (word->alt_choices.size() > 0 && word2->alt_choices.size() > 0) { - // Construct the cartesian product of the alt choices of word(1) and word2. - int num_first_alt_choices = word->alt_choices.size(); - // Nota Bene: For the main loop here, we leave in place word1-only - // alt_choices in - // word->alt_choices[0] .. word_alt_choices[num_first_alt_choices - 1] - // These will get fused with the best choices for word2 below. - for (int j = 1; j < word2->alt_choices.size() && - (j <= kAltsPerPiece || word->alt_choices.size() < kTooManyAltChoices); - j++) { - for (int i = 0; i < num_first_alt_choices && - (i <= kAltsPerPiece || - word->alt_choices.size() < kTooManyAltChoices); - i++) { - WERD_CHOICE *wc = new WERD_CHOICE(*word->alt_choices[i]); - *wc += *word2->alt_choices[j]; - word->alt_choices.push_back(wc); - - word->alt_states.push_back(GenericVector()); - GenericVector &alt_state = word->alt_states.back(); - alt_state += word->alt_states[i]; - alt_state += word2->alt_states[j]; - } - } - // Now that we've filled in as many alternates as we want, paste the best - // choice for word2 onto the original word alt_choices. - for (int i = 0; i < num_first_alt_choices; i++) { - *word->alt_choices[i] += *word2->alt_choices[0]; - word->alt_states[i] += word2->alt_states[0]; + // Construct the cartesian product of the best_choices of word(1) and word2. + WERD_CHOICE_LIST joined_choices; + WERD_CHOICE_IT jc_it(&joined_choices); + WERD_CHOICE_IT bc1_it(&word->best_choices); + WERD_CHOICE_IT bc2_it(&word2->best_choices); + int num_word1_choices = word->best_choices.length(); + int total_joined_choices = num_word1_choices; + // Nota Bene: For the main loop here, we operate only on the 2nd and greater + // word2 choices, and put them in the joined_choices list. The 1st word2 + // choice gets added to the original word1 choices in-place after we have + // finished with them. + int bc2_index = 1; + for (bc2_it.forward(); !bc2_it.at_first(); bc2_it.forward(), ++bc2_index) { + if (total_joined_choices >= kTooManyAltChoices && + bc2_index > kAltsPerPiece) + break; + int bc1_index = 0; + for (bc1_it.move_to_first(); bc1_index < num_word1_choices; + ++bc1_index, bc1_it.forward()) { + if (total_joined_choices >= kTooManyAltChoices && + bc1_index > kAltsPerPiece) + break; + WERD_CHOICE *wc = new WERD_CHOICE(*bc1_it.data()); + *wc += *bc2_it.data(); + jc_it.add_after_then_move(wc); + ++total_joined_choices; } } + // Now that we've filled in as many alternates as we want, paste the best + // choice for word2 onto the original word alt_choices. + bc1_it.move_to_first(); + bc2_it.move_to_first(); + for (bc1_it.mark_cycle_pt(); !bc1_it.cycled_list(); bc1_it.forward()) { + *bc1_it.data() += *bc2_it.data(); + } + bc1_it.move_to_last(); + bc1_it.add_list_after(&joined_choices); // Restore the pointer to original blamer bundle and combine blamer // information recorded in the splits. if (orig_bb != NULL) { - IncorrectResultReason irr = orig_bb->incorrect_result_reason; - if (irr != IRR_NO_TRUTH_SPLIT) blamer_debug = ""; - if (word->blamer_bundle->incorrect_result_reason != IRR_CORRECT && - word->blamer_bundle->incorrect_result_reason != IRR_NO_TRUTH && - word->blamer_bundle->incorrect_result_reason != IRR_NO_TRUTH_SPLIT) { - blamer_debug += "Blame from part 1: "; - blamer_debug += word->blamer_bundle->debug; - irr = word->blamer_bundle->incorrect_result_reason; - } - if (word2->blamer_bundle->incorrect_result_reason != IRR_CORRECT && - word2->blamer_bundle->incorrect_result_reason != IRR_NO_TRUTH && - word2->blamer_bundle->incorrect_result_reason != IRR_NO_TRUTH_SPLIT) { - blamer_debug += "Blame from part 2: "; - blamer_debug += word2->blamer_bundle->debug; - if (irr == IRR_CORRECT) { - irr = word2->blamer_bundle->incorrect_result_reason; - } else if (irr != word2->blamer_bundle->incorrect_result_reason) { - irr = IRR_UNKNOWN; - } - } + orig_bb->JoinBlames(*word->blamer_bundle, *word2->blamer_bundle, + wordrec_debug_blamer); delete word->blamer_bundle; word->blamer_bundle = orig_bb; - word->blamer_bundle->incorrect_result_reason = irr; - if (irr != IRR_CORRECT && irr != IRR_NO_TRUTH) { - word->blamer_bundle->SetBlame(irr, blamer_debug, NULL, - wordrec_debug_blamer); - } } + word->SetupBoxWord(); + word->reject_map.initialise(word->box_word->length()); delete word2; } + } // namespace tesseract diff --git a/ccmain/tfacepp.h b/ccmain/tfacepp.h deleted file mode 100644 index a0d71ec399..0000000000 --- a/ccmain/tfacepp.h +++ /dev/null @@ -1,41 +0,0 @@ -/********************************************************************** - * File: tfacepp.h (Formerly tface++.h) - * Description: C++ side of the C/C++ Tess/Editor interface. - * Author: Ray Smith - * Created: Thu Apr 23 15:39:23 BST 1992 - * - * (C) Copyright 1992, Hewlett-Packard Ltd. - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** http://www.apache.org/licenses/LICENSE-2.0 - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - * - **********************************************************************/ - -#ifndef TFACEPP_H -#define TFACEPP_H - -#include "ratngs.h" -#include "blobs.h" -#include "tesseractclass.h" - -void call_tester( //call a tester - TBLOB *tessblob, //blob to test - BOOL8 correct_blob, //true if good - char *text, //source text - inT32 count, //chars in text - LIST result //output of matcher - ); -void call_train_tester( //call a tester - TBLOB *tessblob, //blob to test - BOOL8 correct_blob, //true if good - char *text, //source text - inT32 count, //chars in text - LIST result //output of matcher - ); -#endif diff --git a/ccmain/werdit.cpp b/ccmain/werdit.cpp index 7aff99e921..7ddacdb55a 100644 --- a/ccmain/werdit.cpp +++ b/ccmain/werdit.cpp @@ -27,7 +27,7 @@ **********************************************************************/ WERD *make_pseudo_word(PAGE_RES* page_res, // Blocks to check. - TBOX &selection_box, + const TBOX &selection_box, BLOCK *&pseudo_block, ROW *&pseudo_row) { // Row of selection. PAGE_RES_IT pr_it(page_res); diff --git a/ccmain/werdit.h b/ccmain/werdit.h index 097e3ac2c3..bb58e67d05 100644 --- a/ccmain/werdit.h +++ b/ccmain/werdit.h @@ -23,7 +23,7 @@ #include "pageres.h" WERD *make_pseudo_word(PAGE_RES* page_res, // blocks to check - TBOX &selection_box, + const TBOX &selection_box, BLOCK *&pseudo_block, ROW *&pseudo_row); diff --git a/ccstruct/Makefile.am b/ccstruct/Makefile.am index 39aa2bf100..0a9f5a81e8 100644 --- a/ccstruct/Makefile.am +++ b/ccstruct/Makefile.am @@ -9,7 +9,7 @@ endif include_HEADERS = publictypes.h noinst_HEADERS = \ - blckerr.h blobbox.h blobs.h blread.h boxread.h boxword.h ccstruct.h coutln.h crakedge.h \ + blamer.h blckerr.h blobbox.h blobs.h blread.h boxread.h boxword.h ccstruct.h coutln.h crakedge.h \ detlinefit.h dppoint.h fontinfo.h genblob.h hpdsizes.h ipoints.h \ linlsq.h matrix.h mod128.h normalis.h \ ocrblock.h ocrpara.h ocrrow.h otsuthr.h \ @@ -31,12 +31,12 @@ libtesseract_ccstruct_la_LIBADD = \ endif libtesseract_ccstruct_la_SOURCES = \ - blobbox.cpp blobs.cpp blread.cpp boxread.cpp boxword.cpp ccstruct.cpp coutln.cpp \ + blamer.cpp blobbox.cpp blobs.cpp blread.cpp boxread.cpp boxword.cpp ccstruct.cpp coutln.cpp \ detlinefit.cpp dppoint.cpp fontinfo.cpp genblob.cpp \ linlsq.cpp matrix.cpp mod128.cpp normalis.cpp \ ocrblock.cpp ocrpara.cpp ocrrow.cpp otsuthr.cpp \ pageres.cpp pdblock.cpp points.cpp polyaprx.cpp polyblk.cpp \ - publictypes.cpp \ + params_training_featdef.cpp publictypes.cpp \ quadlsq.cpp quadratc.cpp quspline.cpp ratngs.cpp rect.cpp rejctmap.cpp \ seam.cpp split.cpp statistc.cpp stepblob.cpp \ vecfuncs.cpp werd.cpp diff --git a/ccstruct/blamer.cpp b/ccstruct/blamer.cpp new file mode 100644 index 0000000000..5d2837d084 --- /dev/null +++ b/ccstruct/blamer.cpp @@ -0,0 +1,587 @@ +/////////////////////////////////////////////////////////////////////// +// File: blamer.cpp +// Description: Module allowing precise error causes to be allocated. +// Author: Rike Antonova +// Refactored: Ray Smith +// Created: Mon Feb 04 14:37:01 PST 2013 +// +// (C) Copyright 2013, Google Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +/////////////////////////////////////////////////////////////////////// + +#include "blamer.h" +#include "blobs.h" +#include "matrix.h" +#include "normalis.h" +#include "pageres.h" + +// Names for each value of IncorrectResultReason enum. Keep in sync. +const char kBlameCorrect[] = "corr"; +const char kBlameClassifier[] = "cl"; +const char kBlameChopper[] = "chop"; +const char kBlameClassLMTradeoff[] = "cl/LM"; +const char kBlamePageLayout[] = "pglt"; +const char kBlameSegsearchHeur[] = "ss_heur"; +const char kBlameSegsearchPP[] = "ss_pp"; +const char kBlameClassOldLMTradeoff[] = "cl/old_LM"; +const char kBlameAdaption[] = "adapt"; +const char kBlameNoTruthSplit[] = "no_tr_spl"; +const char kBlameNoTruth[] = "no_tr"; +const char kBlameUnknown[] = "unkn"; + +const char * const kIncorrectResultReasonNames[] = { + kBlameCorrect, + kBlameClassifier, + kBlameChopper, + kBlameClassLMTradeoff, + kBlamePageLayout, + kBlameSegsearchHeur, + kBlameSegsearchPP, + kBlameClassOldLMTradeoff, + kBlameAdaption, + kBlameNoTruthSplit, + kBlameNoTruth, + kBlameUnknown +}; + +const char *BlamerBundle::IncorrectReasonName(IncorrectResultReason irr) { + return kIncorrectResultReasonNames[irr]; +} + +const char *BlamerBundle::IncorrectReason() const { + return kIncorrectResultReasonNames[incorrect_result_reason_]; +} + +// Functions to setup the blamer. +// Whole word string, whole word bounding box. +void BlamerBundle::SetWordTruth(const UNICHARSET& unicharset, + const char* truth_str, const TBOX& word_box) { + truth_word_.InsertBox(0, word_box); + truth_has_char_boxes_ = false; + // Encode the string as UNICHAR_IDs. + GenericVector encoding; + GenericVector lengths; + unicharset.encode_string(truth_str, false, &encoding, &lengths, NULL); + int total_length = 0; + for (int i = 0; i < encoding.size(); total_length += lengths[i++]) { + STRING uch(truth_str + total_length); + uch.truncate_at(lengths[i] - total_length); + UNICHAR_ID id = encoding[i]; + if (id != INVALID_UNICHAR_ID) uch = unicharset.get_normed_unichar(id); + truth_text_.push_back(uch); + } +} + +// Single "character" string, "character" bounding box. +// May be called multiple times to indicate the characters in a word. +void BlamerBundle::SetSymbolTruth(const UNICHARSET& unicharset, + const char* char_str, const TBOX& char_box) { + STRING symbol_str(char_str); + UNICHAR_ID id = unicharset.unichar_to_id(char_str); + if (id != INVALID_UNICHAR_ID) { + STRING normed_uch(unicharset.get_normed_unichar(id)); + if (normed_uch.length() > 0) symbol_str = normed_uch; + } + int length = truth_word_.length(); + truth_text_.push_back(symbol_str); + truth_word_.InsertBox(length, char_box); + if (length == 0) + truth_has_char_boxes_ = true; + else if (truth_word_.BlobBox(length - 1) == char_box) + truth_has_char_boxes_ = false; +} + +// Marks that there is something wrong with the truth text, like it contains +// reject characters. +void BlamerBundle::SetRejectedTruth() { + incorrect_result_reason_ = IRR_NO_TRUTH; + truth_has_char_boxes_ = false; +} + +// Returns true if the provided word_choice is correct. +bool BlamerBundle::ChoiceIsCorrect(const WERD_CHOICE* word_choice) const { + if (word_choice == NULL) return false; + const UNICHARSET* uni_set = word_choice->unicharset(); + STRING normed_choice_str; + for (int i = 0; i < word_choice->length(); ++i) { + normed_choice_str += + uni_set->get_normed_unichar(word_choice->unichar_id(i)); + } + STRING truth_str = TruthString(); + return truth_str == normed_choice_str; +} + +void BlamerBundle::FillDebugString(const STRING &msg, + const WERD_CHOICE *choice, + STRING *debug) { + (*debug) += "Truth "; + for (int i = 0; i < this->truth_text_.length(); ++i) { + (*debug) += this->truth_text_[i]; + } + if (!this->truth_has_char_boxes_) (*debug) += " (no char boxes)"; + if (choice != NULL) { + (*debug) += " Choice "; + STRING choice_str; + choice->string_and_lengths(&choice_str, NULL); + (*debug) += choice_str; + } + if (msg.length() > 0) { + (*debug) += "\n"; + (*debug) += msg; + } + (*debug) += "\n"; +} + +// Sets up the norm_truth_word from truth_word using the given DENORM. +void BlamerBundle::SetupNormTruthWord(const DENORM& denorm) { + // TODO(rays) Is this the last use of denorm in WERD_RES and can it go? + norm_box_tolerance_ = kBlamerBoxTolerance * denorm.x_scale(); + TPOINT topleft; + TPOINT botright; + TPOINT norm_topleft; + TPOINT norm_botright; + for (int b = 0; b < truth_word_.length(); ++b) { + const TBOX &box = truth_word_.BlobBox(b); + topleft.x = box.left(); + topleft.y = box.top(); + botright.x = box.right(); + botright.y = box.bottom(); + denorm.NormTransform(NULL, topleft, &norm_topleft); + denorm.NormTransform(NULL, botright, &norm_botright); + TBOX norm_box(norm_topleft.x, norm_botright.y, + norm_botright.x, norm_topleft.y); + norm_truth_word_.InsertBox(b, norm_box); + } +} + +// Splits *this into two pieces in bundle1 and bundle2 (preallocated, empty +// bundles) where the right edge/ of the left-hand word is word1_right, +// and the left edge of the right-hand word is word2_left. +void BlamerBundle::SplitBundle(int word1_right, int word2_left, bool debug, + BlamerBundle* bundle1, + BlamerBundle* bundle2) const { + STRING debug_str; + // Find truth boxes that correspond to the split in the blobs. + int b; + int begin2_truth_index = -1; + if (incorrect_result_reason_ != IRR_NO_TRUTH && + truth_has_char_boxes_) { + debug_str = "Looking for truth split at"; + debug_str.add_str_int(" end1_x ", word1_right); + debug_str.add_str_int(" begin2_x ", word2_left); + debug_str += "\nnorm_truth_word boxes:\n"; + if (norm_truth_word_.length() > 1) { + norm_truth_word_.BlobBox(0).print_to_str(&debug_str); + for (b = 1; b < norm_truth_word_.length(); ++b) { + norm_truth_word_.BlobBox(b).print_to_str(&debug_str); + if ((abs(word1_right - norm_truth_word_.BlobBox(b - 1).right()) < + norm_box_tolerance_) && + (abs(word2_left - norm_truth_word_.BlobBox(b).left()) < + norm_box_tolerance_)) { + begin2_truth_index = b; + debug_str += "Split found"; + break; + } + } + debug_str += '\n'; + } + } + // Populate truth information in word and word2 with the first and second + // part of the original truth. + if (begin2_truth_index > 0) { + bundle1->truth_has_char_boxes_ = true; + bundle1->norm_box_tolerance_ = norm_box_tolerance_; + bundle2->truth_has_char_boxes_ = true; + bundle2->norm_box_tolerance_ = norm_box_tolerance_; + BlamerBundle *curr_bb = bundle1; + for (b = 0; b < norm_truth_word_.length(); ++b) { + if (b == begin2_truth_index) curr_bb = bundle2; + curr_bb->norm_truth_word_.InsertBox(b, norm_truth_word_.BlobBox(b)); + curr_bb->truth_word_.InsertBox(b, truth_word_.BlobBox(b)); + curr_bb->truth_text_.push_back(truth_text_[b]); + } + } else if (incorrect_result_reason_ == IRR_NO_TRUTH) { + bundle1->incorrect_result_reason_ = IRR_NO_TRUTH; + bundle2->incorrect_result_reason_ = IRR_NO_TRUTH; + } else { + debug_str += "Truth split not found"; + debug_str += truth_has_char_boxes_ ? + "\n" : " (no truth char boxes)\n"; + bundle1->SetBlame(IRR_NO_TRUTH_SPLIT, debug_str, NULL, debug); + bundle2->SetBlame(IRR_NO_TRUTH_SPLIT, debug_str, NULL, debug); + } +} + +// "Joins" the blames from bundle1 and bundle2 into *this. +void BlamerBundle::JoinBlames(const BlamerBundle& bundle1, + const BlamerBundle& bundle2, bool debug) { + STRING debug_str; + IncorrectResultReason irr = incorrect_result_reason_; + if (irr != IRR_NO_TRUTH_SPLIT) debug_str = ""; + if (bundle1.incorrect_result_reason_ != IRR_CORRECT && + bundle1.incorrect_result_reason_ != IRR_NO_TRUTH && + bundle1.incorrect_result_reason_ != IRR_NO_TRUTH_SPLIT) { + debug_str += "Blame from part 1: "; + debug_str += bundle1.debug_; + irr = bundle1.incorrect_result_reason_; + } + if (bundle2.incorrect_result_reason_ != IRR_CORRECT && + bundle2.incorrect_result_reason_ != IRR_NO_TRUTH && + bundle2.incorrect_result_reason_ != IRR_NO_TRUTH_SPLIT) { + debug_str += "Blame from part 2: "; + debug_str += bundle2.debug_; + if (irr == IRR_CORRECT) { + irr = bundle2.incorrect_result_reason_; + } else if (irr != bundle2.incorrect_result_reason_) { + irr = IRR_UNKNOWN; + } + } + incorrect_result_reason_ = irr; + if (irr != IRR_CORRECT && irr != IRR_NO_TRUTH) { + SetBlame(irr, debug_str, NULL, debug); + } +} + +// If a blob with the same bounding box as one of the truth character +// bounding boxes is not classified as the corresponding truth character +// blames character classifier for incorrect answer. +void BlamerBundle::BlameClassifier(const UNICHARSET& unicharset, + const TBOX& blob_box, + const BLOB_CHOICE_LIST& choices, + bool debug) { + if (!truth_has_char_boxes_ || + incorrect_result_reason_ != IRR_CORRECT) + return; // Nothing to do here. + + for (int b = 0; b < norm_truth_word_.length(); ++b) { + const TBOX &truth_box = norm_truth_word_.BlobBox(b); + // Note that we are more strict on the bounding box boundaries here + // than in other places (chopper, segmentation search), since we do + // not have the ability to check the previous and next bounding box. + if (blob_box.x_almost_equal(truth_box, norm_box_tolerance_/2)) { + bool found = false; + bool incorrect_adapted = false; + UNICHAR_ID incorrect_adapted_id = INVALID_UNICHAR_ID; + const char *truth_str = truth_text_[b].string(); + // We promise not to modify the list or its contents, using a + // const BLOB_CHOICE* below. + BLOB_CHOICE_IT choices_it(const_cast(&choices)); + for (choices_it.mark_cycle_pt(); !choices_it.cycled_list(); + choices_it.forward()) { + const BLOB_CHOICE* choice = choices_it.data(); + if (strcmp(truth_str, unicharset.get_normed_unichar( + choice->unichar_id())) == 0) { + found = true; + break; + } else if (choice->IsAdapted()) { + incorrect_adapted = true; + incorrect_adapted_id = choice->unichar_id(); + } + } // end choices_it for loop + if (!found) { + STRING debug_str = "unichar "; + debug_str += truth_str; + debug_str += " not found in classification list"; + SetBlame(IRR_CLASSIFIER, debug_str, NULL, debug); + } else if (incorrect_adapted) { + STRING debug_str = "better rating for adapted "; + debug_str += unicharset.id_to_unichar(incorrect_adapted_id); + debug_str += " than for correct "; + debug_str += truth_str; + SetBlame(IRR_ADAPTION, debug_str, NULL, debug); + } + break; + } + } // end iterating over blamer_bundle->norm_truth_word +} + +// Checks whether chops were made at all the character bounding box +// boundaries in word->truth_word. If not - blames the chopper for an +// incorrect answer. +void BlamerBundle::SetChopperBlame(const WERD_RES* word, bool debug) { + if (NoTruth() || !truth_has_char_boxes_ || + word->chopped_word->blobs.empty()) { + return; + } + STRING debug_str; + bool missing_chop = false; + int num_blobs = word->chopped_word->blobs.size(); + int box_index = 0; + int blob_index = 0; + inT16 truth_x; + while (box_index < truth_word_.length() && blob_index < num_blobs) { + truth_x = norm_truth_word_.BlobBox(box_index).right(); + TBLOB * curr_blob = word->chopped_word->blobs[blob_index]; + if (curr_blob->bounding_box().right() < truth_x - norm_box_tolerance_) { + ++blob_index; + continue; // encountered an extra chop, keep looking + } else if (curr_blob->bounding_box().right() > + truth_x + norm_box_tolerance_) { + missing_chop = true; + break; + } else { + ++blob_index; + } + } + if (missing_chop || box_index < norm_truth_word_.length()) { + STRING debug_str; + if (missing_chop) { + debug_str.add_str_int("Detected missing chop (tolerance=", + norm_box_tolerance_); + debug_str += ") at Bounding Box="; + TBLOB * curr_blob = word->chopped_word->blobs[blob_index]; + curr_blob->bounding_box().print_to_str(&debug_str); + debug_str.add_str_int("\nNo chop for truth at x=", truth_x); + } else { + debug_str.add_str_int("Missing chops for last ", + norm_truth_word_.length() - box_index); + debug_str += " truth box(es)"; + } + debug_str += "\nMaximally chopped word boxes:\n"; + for (blob_index = 0; blob_index < num_blobs; ++blob_index) { + TBLOB * curr_blob = word->chopped_word->blobs[blob_index]; + curr_blob->bounding_box().print_to_str(&debug_str); + debug_str += '\n'; + } + debug_str += "Truth bounding boxes:\n"; + for (box_index = 0; box_index < norm_truth_word_.length(); ++box_index) { + norm_truth_word_.BlobBox(box_index).print_to_str(&debug_str); + debug_str += '\n'; + } + SetBlame(IRR_CHOPPER, debug_str, word->best_choice, debug); + } +} + +// Blames the classifier or the language model if, after running only the +// chopper, best_choice is incorrect and no blame has been yet set. +// Blames the classifier if best_choice is classifier's top choice and is a +// dictionary word (i.e. language model could not have helped). +// Otherwise, blames the language model (formerly permuter word adjustment). +void BlamerBundle::BlameClassifierOrLangModel( + const WERD_RES* word, + const UNICHARSET& unicharset, bool valid_permuter, bool debug) { + if (valid_permuter) { + // Find out whether best choice is a top choice. + best_choice_is_dict_and_top_choice_ = true; + for (int i = 0; i < word->best_choice->length(); ++i) { + BLOB_CHOICE_IT blob_choice_it(word->GetBlobChoices(i)); + ASSERT_HOST(!blob_choice_it.empty()); + BLOB_CHOICE *first_choice = NULL; + for (blob_choice_it.mark_cycle_pt(); !blob_choice_it.cycled_list(); + blob_choice_it.forward()) { // find first non-fragment choice + if (!(unicharset.get_fragment(blob_choice_it.data()->unichar_id()))) { + first_choice = blob_choice_it.data(); + break; + } + } + ASSERT_HOST(first_choice != NULL); + if (first_choice->unichar_id() != word->best_choice->unichar_id(i)) { + best_choice_is_dict_and_top_choice_ = false; + break; + } + } + } + STRING debug_str; + if (best_choice_is_dict_and_top_choice_) { + debug_str = "Best choice is: incorrect, top choice, dictionary word"; + debug_str += " with permuter "; + debug_str += word->best_choice->permuter_name(); + } else { + debug_str = "Classifier/Old LM tradeoff is to blame"; + } + SetBlame(best_choice_is_dict_and_top_choice_ ? IRR_CLASSIFIER + : IRR_CLASS_OLD_LM_TRADEOFF, + debug_str, word->best_choice, debug); +} + +// Sets up the correct_segmentation_* to mark the correct bounding boxes. +void BlamerBundle::SetupCorrectSegmentation(const TWERD* word, bool debug) { + params_training_bundle_.StartHypothesisList(); + if (incorrect_result_reason_ != IRR_CORRECT || !truth_has_char_boxes_) + return; // Nothing to do here. + + STRING debug_str; + debug_str += "Blamer computing correct_segmentation_cols\n"; + int curr_box_col = 0; + int next_box_col = 0; + int num_blobs = word->NumBlobs(); + if (num_blobs == 0) return; // No blobs to play with. + int blob_index = 0; + inT16 next_box_x = word->blobs[blob_index]->bounding_box().right(); + for (int truth_idx = 0; blob_index < num_blobs && + truth_idx < norm_truth_word_.length(); + ++blob_index) { + ++next_box_col; + inT16 curr_box_x = next_box_x; + if (blob_index + 1 < num_blobs) + next_box_x = word->blobs[blob_index + 1]->bounding_box().right(); + inT16 truth_x = norm_truth_word_.BlobBox(truth_idx).right(); + debug_str.add_str_int("Box x coord vs. truth: ", curr_box_x); + debug_str.add_str_int(" ", truth_x); + debug_str += "\n"; + if (curr_box_x > (truth_x + norm_box_tolerance_)) { + break; // failed to find a matching box + } else if (curr_box_x >= truth_x - norm_box_tolerance_ && // matched + (blob_index + 1 >= num_blobs || // next box can't be included + next_box_x > truth_x + norm_box_tolerance_)) { + correct_segmentation_cols_.push_back(curr_box_col); + correct_segmentation_rows_.push_back(next_box_col-1); + ++truth_idx; + debug_str.add_str_int("col=", curr_box_col); + debug_str.add_str_int(" row=", next_box_col-1); + debug_str += "\n"; + curr_box_col = next_box_col; + } + } + if (blob_index < num_blobs || // trailing blobs + correct_segmentation_cols_.length() != norm_truth_word_.length()) { + debug_str.add_str_int("Blamer failed to find correct segmentation" + " (tolerance=", norm_box_tolerance_); + if (blob_index >= num_blobs) debug_str += " blob == NULL"; + debug_str += ")\n"; + debug_str.add_str_int(" path length ", correct_segmentation_cols_.length()); + debug_str.add_str_int(" vs. truth ", norm_truth_word_.length()); + debug_str += "\n"; + SetBlame(IRR_UNKNOWN, debug_str, NULL, debug); + correct_segmentation_cols_.clear(); + correct_segmentation_rows_.clear(); + } +} + +// Returns true if a guided segmentation search is needed. +bool BlamerBundle::GuidedSegsearchNeeded(const WERD_CHOICE *best_choice) const { + return incorrect_result_reason_ == IRR_CORRECT && + !segsearch_is_looking_for_blame_ && + truth_has_char_boxes_ && + !ChoiceIsCorrect(best_choice); +} + +// Setup ready to guide the segmentation search to the correct segmentation. +// The callback pp_cb is used to avoid a cyclic dependency. +// It calls into LMPainPoints::GenerateForBlamer by pre-binding the +// WERD_RES, and the LMPainPoints itself. +// pp_cb must be a permanent callback, and should be deleted by the caller. +void BlamerBundle::InitForSegSearch(const WERD_CHOICE *best_choice, + MATRIX* ratings, UNICHAR_ID wildcard_id, + bool debug, STRING *debug_str, + TessResultCallback2* cb) { + segsearch_is_looking_for_blame_ = true; + if (debug) { + tprintf("segsearch starting to look for blame\n"); + } + // Fill pain points for any unclassifed blob corresponding to the + // correct segmentation state. + *debug_str += "Correct segmentation:\n"; + for (int idx = 0; idx < correct_segmentation_cols_.length(); ++idx) { + debug_str->add_str_int("col=", correct_segmentation_cols_[idx]); + debug_str->add_str_int(" row=", correct_segmentation_rows_[idx]); + *debug_str += "\n"; + if (!ratings->Classified(correct_segmentation_cols_[idx], + correct_segmentation_rows_[idx], + wildcard_id) && + !cb->Run(correct_segmentation_cols_[idx], + correct_segmentation_rows_[idx])) { + segsearch_is_looking_for_blame_ = false; + *debug_str += "\nFailed to insert pain point\n"; + SetBlame(IRR_SEGSEARCH_HEUR, *debug_str, best_choice, debug); + break; + } + } // end for blamer_bundle->correct_segmentation_cols/rows +} +// Returns true if the guided segsearch is in progress. +bool BlamerBundle::GuidedSegsearchStillGoing() const { + return segsearch_is_looking_for_blame_; +} + +// The segmentation search has ended. Sets the blame appropriately. +void BlamerBundle::FinishSegSearch(const WERD_CHOICE *best_choice, + bool debug, STRING *debug_str) { + // If we are still looking for blame (i.e. best_choice is incorrect, but a + // path representing the correct segmentation could be constructed), we can + // blame segmentation search pain point prioritization if the rating of the + // path corresponding to the correct segmentation is better than that of + // best_choice (i.e. language model would have done the correct thing, but + // because of poor pain point prioritization the correct segmentation was + // never explored). Otherwise we blame the tradeoff between the language model + // and the classifier, since even after exploring the path corresponding to + // the correct segmentation incorrect best_choice would have been chosen. + // One special case when we blame the classifier instead is when best choice + // is incorrect, but it is a dictionary word and it classifier's top choice. + if (segsearch_is_looking_for_blame_) { + segsearch_is_looking_for_blame_ = false; + if (best_choice_is_dict_and_top_choice_) { + *debug_str = "Best choice is: incorrect, top choice, dictionary word"; + *debug_str += " with permuter "; + *debug_str += best_choice->permuter_name(); + SetBlame(IRR_CLASSIFIER, *debug_str, best_choice, debug); + } else if (best_correctly_segmented_rating_ < + best_choice->rating()) { + *debug_str += "Correct segmentation state was not explored"; + SetBlame(IRR_SEGSEARCH_PP, *debug_str, best_choice, debug); + } else { + if (best_correctly_segmented_rating_ >= + WERD_CHOICE::kBadRating) { + *debug_str += "Correct segmentation paths were pruned by LM\n"; + } else { + debug_str->add_str_double("Best correct segmentation rating ", + best_correctly_segmented_rating_); + debug_str->add_str_double(" vs. best choice rating ", + best_choice->rating()); + } + SetBlame(IRR_CLASS_LM_TRADEOFF, *debug_str, best_choice, debug); + } + } +} + +// If the bundle is null or still does not indicate the correct result, +// fix it and use some backup reason for the blame. +void BlamerBundle::LastChanceBlame(bool debug, WERD_RES* word) { + if (word->blamer_bundle == NULL) { + word->blamer_bundle = new BlamerBundle(); + word->blamer_bundle->SetBlame(IRR_PAGE_LAYOUT, "LastChanceBlame", + word->best_choice, debug); + } else if (word->blamer_bundle->incorrect_result_reason_ == IRR_NO_TRUTH) { + word->blamer_bundle->SetBlame(IRR_NO_TRUTH, "Rejected truth", + word->best_choice, debug); + } else { + bool correct = word->blamer_bundle->ChoiceIsCorrect(word->best_choice); + IncorrectResultReason irr = word->blamer_bundle->incorrect_result_reason_; + if (irr == IRR_CORRECT && !correct) { + STRING debug_str = "Choice is incorrect after recognition"; + word->blamer_bundle->SetBlame(IRR_UNKNOWN, debug_str, word->best_choice, + debug); + } else if (irr != IRR_CORRECT && correct) { + if (debug) { + tprintf("Corrected %s\n", word->blamer_bundle->debug_.string()); + } + word->blamer_bundle->incorrect_result_reason_ = IRR_CORRECT; + word->blamer_bundle->debug_ = ""; + } + } +} + +// Sets the misadaption debug if this word is incorrect, as this word is +// being adapted to. +void BlamerBundle::SetMisAdaptionDebug(const WERD_CHOICE *best_choice, + bool debug) { + if (incorrect_result_reason_ != IRR_NO_TRUTH && + !ChoiceIsCorrect(best_choice)) { + misadaption_debug_ ="misadapt to word ("; + misadaption_debug_ += best_choice->permuter_name(); + misadaption_debug_ += "): "; + FillDebugString("", best_choice, &misadaption_debug_); + if (debug) { + tprintf("%s\n", misadaption_debug_.string()); + } + } +} + diff --git a/ccstruct/blamer.h b/ccstruct/blamer.h new file mode 100644 index 0000000000..72ba3a24f7 --- /dev/null +++ b/ccstruct/blamer.h @@ -0,0 +1,330 @@ +/////////////////////////////////////////////////////////////////////// +// File: blamer.h +// Description: Module allowing precise error causes to be allocated. +// Author: Rike Antonova +// Refactored: Ray Smith +// Created: Mon Feb 04 14:37:01 PST 2013 +// +// (C) Copyright 2013, Google Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +/////////////////////////////////////////////////////////////////////// + +#ifndef TESSERACT_CCSTRUCT_BLAMER_H_ +#define TESSERACT_CCSTRUCT_BLAMER_H_ + +#include +#include "boxword.h" +#include "genericvector.h" +#include "matrix.h" +#include "params_training_featdef.h" +#include "ratngs.h" +#include "strngs.h" +#include "tesscallback.h" + +static const inT16 kBlamerBoxTolerance = 5; + +// Enum for expressing the source of error. +// Note: Please update kIncorrectResultReasonNames when modifying this enum. +enum IncorrectResultReason { + // The text recorded in best choice == truth text + IRR_CORRECT, + // Either: Top choice is incorrect and is a dictionary word (language model + // is unlikely to help correct such errors, so blame the classifier). + // Or: the correct unichar was not included in shortlist produced by the + // classifier at all. + IRR_CLASSIFIER, + // Chopper have not found one or more splits that correspond to the correct + // character bounding boxes recorded in BlamerBundle::truth_word. + IRR_CHOPPER, + // Classifier did include correct unichars for each blob in the correct + // segmentation, however its rating could have been too bad to allow the + // language model to pull out the correct choice. On the other hand the + // strength of the language model might have been too weak to favor the + // correct answer, this we call this case a classifier-language model + // tradeoff error. + IRR_CLASS_LM_TRADEOFF, + // Page layout failed to produce the correct bounding box. Blame page layout + // if the truth was not found for the word, which implies that the bounding + // box of the word was incorrect (no truth word had a similar bounding box). + IRR_PAGE_LAYOUT, + // SegSearch heuristic prevented one or more blobs from the correct + // segmentation state to be classified (e.g. the blob was too wide). + IRR_SEGSEARCH_HEUR, + // The correct segmentaiton state was not explored because of poor SegSearch + // pain point prioritization. We blame SegSearch pain point prioritization + // if the best rating of a choice constructed from correct segmentation is + // better than that of the best choice (i.e. if we got to explore the correct + // segmentation state, language model would have picked the correct choice). + IRR_SEGSEARCH_PP, + // Same as IRR_CLASS_LM_TRADEOFF, but used when we only run chopper on a word, + // and thus use the old language model (permuters). + // TODO(antonova): integrate the new language mode with chopper + IRR_CLASS_OLD_LM_TRADEOFF, + // If there is an incorrect adaptive template match with a better score than + // a correct one (either pre-trained or adapted), mark this as adaption error. + IRR_ADAPTION, + // split_and_recog_word() failed to find a suitable split in truth. + IRR_NO_TRUTH_SPLIT, + // Truth is not available for this word (e.g. when words in corrected content + // file are turned into ~~~~ because an appropriate alignment was not found. + IRR_NO_TRUTH, + // The text recorded in best choice != truth text, but none of the above + // reasons are set. + IRR_UNKNOWN, + + IRR_NUM_REASONS +}; + +// Blamer-related information to determine the source of errors. +struct BlamerBundle { + static const char *IncorrectReasonName(IncorrectResultReason irr); + BlamerBundle() : truth_has_char_boxes_(false), + incorrect_result_reason_(IRR_CORRECT), + lattice_data_(NULL) { ClearResults(); } + BlamerBundle(const BlamerBundle &other) { + this->CopyTruth(other); + this->CopyResults(other); + } + ~BlamerBundle() { delete[] lattice_data_; } + + // Accessors. + STRING TruthString() const { + STRING truth_str; + for (int i = 0; i < truth_text_.length(); ++i) + truth_str += truth_text_[i]; + return truth_str; + } + IncorrectResultReason incorrect_result_reason() const { + return incorrect_result_reason_; + } + bool NoTruth() const { + return incorrect_result_reason_ == IRR_NO_TRUTH || + incorrect_result_reason_ == IRR_PAGE_LAYOUT; + } + bool HasDebugInfo() const { + return debug_.length() > 0 || misadaption_debug_.length() > 0; + } + const STRING& debug() const { + return debug_; + } + const STRING& misadaption_debug() const { + return misadaption_debug_; + } + void UpdateBestRating(float rating) { + if (rating < best_correctly_segmented_rating_) + best_correctly_segmented_rating_ = rating; + } + int correct_segmentation_length() const { + return correct_segmentation_cols_.length(); + } + // Returns true if the given ratings matrix col,row position is included + // in the correct segmentation path at the given index. + bool MatrixPositionCorrect(int index, const MATRIX_COORD& coord) { + return correct_segmentation_cols_[index] == coord.col && + correct_segmentation_rows_[index] == coord.row; + } + void set_best_choice_is_dict_and_top_choice(bool value) { + best_choice_is_dict_and_top_choice_ = value; + } + const char* lattice_data() const { + return lattice_data_; + } + int lattice_size() const { + return lattice_size_; // size of lattice_data in bytes + } + void set_lattice_data(const char* data, int size) { + lattice_size_ = size; + delete [] lattice_data_; + lattice_data_ = new char[lattice_size_]; + memcpy(lattice_data_, data, lattice_size_); + } + const tesseract::ParamsTrainingBundle& params_training_bundle() const { + return params_training_bundle_; + } + // Adds a new ParamsTrainingHypothesis to the current hypothesis list. + void AddHypothesis(const tesseract::ParamsTrainingHypothesis& hypo) { + params_training_bundle_.AddHypothesis(hypo); + } + + // Functions to setup the blamer. + // Whole word string, whole word bounding box. + void SetWordTruth(const UNICHARSET& unicharset, + const char* truth_str, const TBOX& word_box); + // Single "character" string, "character" bounding box. + // May be called multiple times to indicate the characters in a word. + void SetSymbolTruth(const UNICHARSET& unicharset, + const char* char_str, const TBOX& char_box); + // Marks that there is something wrong with the truth text, like it contains + // reject characters. + void SetRejectedTruth(); + + // Returns true if the provided word_choice is correct. + bool ChoiceIsCorrect(const WERD_CHOICE* word_choice) const; + + void ClearResults() { + norm_truth_word_.DeleteAllBoxes(); + norm_box_tolerance_ = 0; + if (!NoTruth()) incorrect_result_reason_ = IRR_CORRECT; + debug_ = ""; + segsearch_is_looking_for_blame_ = false; + best_correctly_segmented_rating_ = WERD_CHOICE::kBadRating; + correct_segmentation_cols_.clear(); + correct_segmentation_rows_.clear(); + best_choice_is_dict_and_top_choice_ = false; + delete[] lattice_data_; + lattice_data_ = NULL; + lattice_size_ = 0; + } + void CopyTruth(const BlamerBundle &other) { + truth_has_char_boxes_ = other.truth_has_char_boxes_; + truth_word_ = other.truth_word_; + truth_text_ = other.truth_text_; + incorrect_result_reason_ = + (other.NoTruth() ? other.incorrect_result_reason_ : IRR_CORRECT); + } + void CopyResults(const BlamerBundle &other) { + norm_truth_word_ = other.norm_truth_word_; + norm_box_tolerance_ = other.norm_box_tolerance_; + incorrect_result_reason_ = other.incorrect_result_reason_; + segsearch_is_looking_for_blame_ = other.segsearch_is_looking_for_blame_; + best_correctly_segmented_rating_ = other.best_correctly_segmented_rating_; + correct_segmentation_cols_ = other.correct_segmentation_cols_; + correct_segmentation_rows_ = other.correct_segmentation_rows_; + best_choice_is_dict_and_top_choice_ = + other.best_choice_is_dict_and_top_choice_; + if (other.lattice_data_ != NULL) { + lattice_data_ = new char[other.lattice_size_]; + memcpy(lattice_data_, other.lattice_data_, other.lattice_size_); + lattice_size_ = other.lattice_size_; + } else { + lattice_data_ = NULL; + } + } + const char *IncorrectReason() const; + + // Appends choice and truth details to the given debug string. + void FillDebugString(const STRING &msg, const WERD_CHOICE *choice, + STRING *debug); + + // Sets up the norm_truth_word from truth_word using the given DENORM. + void SetupNormTruthWord(const DENORM& denorm); + + // Splits *this into two pieces in bundle1 and bundle2 (preallocated, empty + // bundles) where the right edge/ of the left-hand word is word1_right, + // and the left edge of the right-hand word is word2_left. + void SplitBundle(int word1_right, int word2_left, bool debug, + BlamerBundle* bundle1, BlamerBundle* bundle2) const; + // "Joins" the blames from bundle1 and bundle2 into *this. + void JoinBlames(const BlamerBundle& bundle1, const BlamerBundle& bundle2, + bool debug); + + // If a blob with the same bounding box as one of the truth character + // bounding boxes is not classified as the corresponding truth character + // blames character classifier for incorrect answer. + void BlameClassifier(const UNICHARSET& unicharset, + const TBOX& blob_box, + const BLOB_CHOICE_LIST& choices, + bool debug); + + + // Checks whether chops were made at all the character bounding box + // boundaries in word->truth_word. If not - blames the chopper for an + // incorrect answer. + void SetChopperBlame(const WERD_RES* word, bool debug); + // Blames the classifier or the language model if, after running only the + // chopper, best_choice is incorrect and no blame has been yet set. + // Blames the classifier if best_choice is classifier's top choice and is a + // dictionary word (i.e. language model could not have helped). + // Otherwise, blames the language model (formerly permuter word adjustment). + void BlameClassifierOrLangModel( + const WERD_RES* word, + const UNICHARSET& unicharset, bool valid_permuter, bool debug); + // Sets up the correct_segmentation_* to mark the correct bounding boxes. + void SetupCorrectSegmentation(const TWERD* word, bool debug); + + // Returns true if a guided segmentation search is needed. + bool GuidedSegsearchNeeded(const WERD_CHOICE *best_choice) const; + // Setup ready to guide the segmentation search to the correct segmentation. + // The callback pp_cb is used to avoid a cyclic dependency. + // It calls into LMPainPoints::GenerateForBlamer by pre-binding the + // WERD_RES, and the LMPainPoints itself. + // pp_cb must be a permanent callback, and should be deleted by the caller. + void InitForSegSearch(const WERD_CHOICE *best_choice, + MATRIX* ratings, UNICHAR_ID wildcard_id, + bool debug, STRING *debug_str, + TessResultCallback2* pp_cb); + // Returns true if the guided segsearch is in progress. + bool GuidedSegsearchStillGoing() const; + // The segmentation search has ended. Sets the blame appropriately. + void FinishSegSearch(const WERD_CHOICE *best_choice, + bool debug, STRING *debug_str); + + // If the bundle is null or still does not indicate the correct result, + // fix it and use some backup reason for the blame. + static void LastChanceBlame(bool debug, WERD_RES* word); + + // Sets the misadaption debug if this word is incorrect, as this word is + // being adapted to. + void SetMisAdaptionDebug(const WERD_CHOICE *best_choice, bool debug); + + private: + void SetBlame(IncorrectResultReason irr, const STRING &msg, + const WERD_CHOICE *choice, bool debug) { + incorrect_result_reason_ = irr; + debug_ = IncorrectReason(); + debug_ += " to blame: "; + FillDebugString(msg, choice, &debug_); + if (debug) tprintf("SetBlame(): %s", debug_.string()); + } + + private: + // Set to true when bounding boxes for individual unichars are recorded. + bool truth_has_char_boxes_; + // The true_word (in the original image coordinate space) contains ground + // truth bounding boxes for this WERD_RES. + tesseract::BoxWord truth_word_; + // Same as above, but in normalized coordinates + // (filled in by WERD_RES::SetupForRecognition()). + tesseract::BoxWord norm_truth_word_; + // Tolerance for bounding box comparisons in normalized space. + int norm_box_tolerance_; + // Contains ground truth unichar for each of the bounding boxes in truth_word. + GenericVector truth_text_; + // The reason for incorrect OCR result. + IncorrectResultReason incorrect_result_reason_; + // Debug text associated with the blame. + STRING debug_; + // Misadaption debug information (filled in if this word was misadapted to). + STRING misadaption_debug_; + // Variables used by the segmentation search when looking for the blame. + // Set to true while segmentation search is continued after the usual + // termination condition in order to look for the blame. + bool segsearch_is_looking_for_blame_; + // Best rating for correctly segmented path + // (set and used by SegSearch when looking for blame). + float best_correctly_segmented_rating_; + // Vectors populated by SegSearch to indicate column and row indices that + // correspond to blobs with correct bounding boxes. + GenericVector correct_segmentation_cols_; + GenericVector correct_segmentation_rows_; + // Set to true if best choice is a dictionary word and + // classifier's top choice. + bool best_choice_is_dict_and_top_choice_; + // Serialized segmentation search lattice. + char *lattice_data_; + int lattice_size_; // size of lattice_data in bytes + // Information about hypotheses (paths) explored by the segmentation search. + tesseract::ParamsTrainingBundle params_training_bundle_; +}; + + +#endif // TESSERACT_CCSTRUCT_BLAMER_H_ diff --git a/ccstruct/boxword.cpp b/ccstruct/boxword.cpp index 95a8258c53..c76966667c 100644 --- a/ccstruct/boxword.cpp +++ b/ccstruct/boxword.cpp @@ -29,12 +29,6 @@ namespace tesseract { // tolerance. Otherwise, the blob may be chopped and we have to just use // the word bounding box. const int kBoxClipTolerance = 2; -// Min offset in baseline-normalized coords to make a character a subscript. -const int kMinSubscriptOffset = 20; -// Min offset in baseline-normalized coords to make a character a superscript. -const int kMinSuperscriptOffset = 20; -// Max y of bottom of a drop-cap blob. -const int kMaxDropCapBottom = -128; BoxWord::BoxWord() : length_(0) { } @@ -60,21 +54,17 @@ void BoxWord::CopyFrom(const BoxWord& src) { boxes_.push_back(src.boxes_[i]); } -// Factory to build a BoxWord from a TWERD and the DENORM to switch -// back to original image coordinates. -// If the denorm is not NULL, then the output is denormalized and rotated -// back to the original image coordinates. -BoxWord* BoxWord::CopyFromNormalized(const DENORM* denorm, - TWERD* tessword) { +// Factory to build a BoxWord from a TWERD using the DENORMs on each blob to +// switch back to original image coordinates. +BoxWord* BoxWord::CopyFromNormalized(TWERD* tessword) { BoxWord* boxword = new BoxWord(); // Count the blobs. - boxword->length_ = 0; - for (TBLOB* tblob = tessword->blobs; tblob != NULL; tblob = tblob->next) - ++boxword->length_; + boxword->length_ = tessword->NumBlobs(); // Allocate memory. boxword->boxes_.reserve(boxword->length_); - for (TBLOB* tblob = tessword->blobs; tblob != NULL; tblob = tblob->next) { + for (int b = 0; b < boxword->length_; ++b) { + TBLOB* tblob = tessword->blobs[b]; TBOX blob_box; for (TESSLINE* outline = tblob->outlines; outline != NULL; outline = outline->next) { @@ -83,12 +73,10 @@ BoxWord* BoxWord::CopyFromNormalized(const DENORM* denorm, do { if (!edgept->IsHidden() || !edgept->prev->IsHidden()) { ICOORD pos(edgept->pos.x, edgept->pos.y); - if (denorm != NULL) { - TPOINT denormed; - denorm->DenormTransform(edgept->pos, &denormed); - pos.set_x(denormed.x); - pos.set_y(denormed.y); - } + TPOINT denormed; + tblob->denorm().DenormTransform(NULL, edgept->pos, &denormed); + pos.set_x(denormed.x); + pos.set_y(denormed.y); TBOX pt_box(pos, pos); blob_box += pt_box; } @@ -101,37 +89,6 @@ BoxWord* BoxWord::CopyFromNormalized(const DENORM* denorm, return boxword; } -// Sets up the script_pos_ member using the tessword to get the bln -// bounding boxes, the best_choice to get the unichars, and the unicharset -// to get the target positions. If small_caps is true, sub/super are not -// considered, but dropcaps are. -void BoxWord::SetScriptPositions(const UNICHARSET& unicharset, bool small_caps, - TWERD* tessword, WERD_CHOICE* best_choice) { - // Allocate memory. - script_pos_.init_to_size(length_, SP_NORMAL); - - int blob_index = 0; - for (TBLOB* tblob = tessword->blobs; tblob != NULL; tblob = tblob->next, - ++blob_index) { - int class_id = best_choice->unichar_id(blob_index); - TBOX blob_box = tblob->bounding_box(); - int top = blob_box.top(); - int bottom = blob_box.bottom(); - int min_bottom, max_bottom, min_top, max_top; - unicharset.get_top_bottom(class_id, &min_bottom, &max_bottom, - &min_top, &max_top); - if (bottom <= kMaxDropCapBottom) { - script_pos_[blob_index] = SP_DROPCAP; - } else if (!small_caps) { - if (top + kMinSubscriptOffset < min_top) { - script_pos_[blob_index] = SP_SUBSCRIPT; - } else if (bottom - kMinSuperscriptOffset > max_bottom) { - script_pos_[blob_index] = SP_SUPERSCRIPT; - } - } - } -} - // Clean up the bounding boxes from the polygonal approximation by // expanding slightly, then clipping to the blobs from the original_word // that overlap. If not null, the block provides the inverse rotation. @@ -228,9 +185,8 @@ void BoxWord::ComputeBoundingBox() { // The callback is deleted on completion. void BoxWord::ProcessMatchedBlobs(const TWERD& other, TessCallback1* cb) const { - TBLOB* blob = other.blobs; - for (int i = 0; i < length_ && blob != NULL; ++i, blob = blob->next) { - TBOX blob_box = blob->bounding_box(); + for (int i = 0; i < length_ && i < other.NumBlobs(); ++i) { + TBOX blob_box = other.blobs[i]->bounding_box(); if (blob_box == boxes_[i]) cb->Run(i); } @@ -238,5 +194,3 @@ void BoxWord::ProcessMatchedBlobs(const TWERD& other, } } // namespace tesseract. - - diff --git a/ccstruct/boxword.h b/ccstruct/boxword.h index bab2d7ee9e..6dc1719a4f 100644 --- a/ccstruct/boxword.h +++ b/ccstruct/boxword.h @@ -22,6 +22,7 @@ #include "genericvector.h" #include "rect.h" +#include "unichar.h" class BLOCK; class DENORM; @@ -34,14 +35,6 @@ class WERD_RES; namespace tesseract { -// ScriptPos tells whether a character is subscript, superscript or normal. -enum ScriptPos { - SP_NORMAL, - SP_SUBSCRIPT, - SP_SUPERSCRIPT, - SP_DROPCAP -}; - // Class to hold an array of bounding boxes for an output word and // the bounding box of the whole word. class BoxWord { @@ -54,19 +47,9 @@ class BoxWord { void CopyFrom(const BoxWord& src); - // Factory to build a BoxWord from a TWERD and the DENORM to switch - // back to original image coordinates. - // If the denorm is not NULL, then the output is denormalized and rotated - // back to the original image coordinates. - static BoxWord* CopyFromNormalized(const DENORM* denorm, - TWERD* tessword); - - // Sets up the script_pos_ member using the tessword to get the bln - // bounding boxes, the best_choice to get the unichars, and the unicharset - // to get the target positions. If small_caps is true, sub/super are not - // considered, but dropcaps are. - void SetScriptPositions(const UNICHARSET& unicharset, bool small_caps, - TWERD* tessword, WERD_CHOICE* best_choice); + // Factory to build a BoxWord from a TWERD using the DENORMs on each blob to + // switch back to original image coordinates. + static BoxWord* CopyFromNormalized(TWERD* tessword); // Clean up the bounding boxes from the polygonal approximation by // expanding slightly, then clipping to the blobs from the original_word @@ -102,11 +85,6 @@ class BoxWord { const TBOX& BlobBox(int index) const { return boxes_[index]; } - ScriptPos BlobPosition(int index) const { - if (index < 0 || index >= script_pos_.size()) - return SP_NORMAL; - return script_pos_[index]; - } private: void ComputeBoundingBox(); @@ -114,7 +92,6 @@ class BoxWord { TBOX bbox_; int length_; GenericVector boxes_; - GenericVector script_pos_; }; } // namespace tesseract. diff --git a/ccstruct/matrix.cpp b/ccstruct/matrix.cpp index 377b406fb7..282be97e0d 100644 --- a/ccstruct/matrix.cpp +++ b/ccstruct/matrix.cpp @@ -32,21 +32,120 @@ #include "tprintf.h" #include "unicharset.h" +// Returns true if there are any real classification results. +bool MATRIX::Classified(int col, int row, int wildcard_id) const { + if (get(col, row) == NOT_CLASSIFIED) return false; + BLOB_CHOICE_IT b_it(get(col, row)); + for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) { + BLOB_CHOICE* choice = b_it.data(); + if (choice->IsClassified()) + return true; + } + return false; +} + +// Expands the existing matrix in-place to make the band wider, without +// losing any existing data. +void MATRIX::IncreaseBandSize(int bandwidth) { + ResizeWithCopy(dimension(), bandwidth); +} + +// Returns a bigger MATRIX with a new column and row in the matrix in order +// to split the blob at the given (ind,ind) diagonal location. +// Entries are relocated to the new MATRIX using the transformation defined +// by MATRIX_COORD::MapForSplit. +// Transfers the pointer data to the new MATRIX and deletes *this. +MATRIX* MATRIX::ConsumeAndMakeBigger(int ind) { + int dim = dimension(); + int band_width = bandwidth(); + // Check to see if bandwidth needs expanding. + for (int col = ind; col >= 0 && col > ind - band_width; --col) { + if (array_[col * band_width + band_width - 1] != empty_) { + ++band_width; + break; + } + } + MATRIX* result = new MATRIX(dim + 1, band_width); + + for (int col = 0; col < dim; ++col) { + for (int row = col; row < dim && row < col + bandwidth(); ++row) { + MATRIX_COORD coord(col, row); + coord.MapForSplit(ind); + BLOB_CHOICE_LIST* choices = get(col, row); + if (choices != NULL) { + // Correct matrix location on each choice. + BLOB_CHOICE_IT bc_it(choices); + for (bc_it.mark_cycle_pt(); !bc_it.cycled_list(); bc_it.forward()) { + BLOB_CHOICE* choice = bc_it.data(); + choice->set_matrix_cell(coord.col, coord.row); + } + ASSERT_HOST(coord.Valid(*result)); + result->put(coord.col, coord.row, choices); + } + } + } + delete this; + return result; +} + +// Makes and returns a deep copy of *this, including all the BLOB_CHOICEs +// on the lists, but not any LanguageModelState that may be attached to the +// BLOB_CHOICEs. +MATRIX* MATRIX::DeepCopy() const { + int dim = dimension(); + int band_width = bandwidth(); + MATRIX* result = new MATRIX(dim, band_width); + for (int col = 0; col < dim; ++col) { + for (int row = col; row < col + band_width; ++row) { + BLOB_CHOICE_LIST* choices = get(col, row); + if (choices != NULL) { + BLOB_CHOICE_LIST* copy_choices = new BLOB_CHOICE_LIST; + choices->deep_copy(copy_choices, &BLOB_CHOICE::deep_copy); + result->put(col, row, copy_choices); + } + } + } + return result; +} + // Print the best guesses out of the match rating matrix. void MATRIX::print(const UNICHARSET &unicharset) const { - tprintf("Ratings Matrix (top choices)\n"); + tprintf("Ratings Matrix (top 3 choices)\n"); + int dim = dimension(); + int band_width = bandwidth(); int row, col; - for (col = 0; col < this->dimension(); ++col) tprintf("\t%d", col); + for (col = 0; col < dim; ++col) { + for (row = col; row < dim && row < col + band_width; ++row) { + BLOB_CHOICE_LIST *rating = this->get(col, row); + if (rating == NOT_CLASSIFIED) continue; + BLOB_CHOICE_IT b_it(rating); + tprintf("col=%d row=%d ", col, row); + for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) { + tprintf("%s rat=%g cert=%g " , + unicharset.id_to_unichar(b_it.data()->unichar_id()), + b_it.data()->rating(), b_it.data()->certainty()); + } + tprintf("\n"); + } + tprintf("\n"); + } tprintf("\n"); - for (row = 0; row < this->dimension(); ++row) { + for (col = 0; col < dim; ++col) tprintf("\t%d", col); + tprintf("\n"); + for (row = 0; row < dim; ++row) { for (col = 0; col <= row; ++col) { if (col == 0) tprintf("%d\t", row); + if (row >= col + band_width) { + tprintf(" \t"); + continue; + } BLOB_CHOICE_LIST *rating = this->get(col, row); if (rating != NOT_CLASSIFIED) { BLOB_CHOICE_IT b_it(rating); int counter = 0; for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) { - tprintf("%s ", unicharset.id_to_unichar(b_it.data()->unichar_id())); + tprintf("%s ", + unicharset.id_to_unichar(b_it.data()->unichar_id())); ++counter; if (counter == 3) break; } diff --git a/ccstruct/matrix.h b/ccstruct/matrix.h index 8b34711ceb..af240b07d3 100644 --- a/ccstruct/matrix.h +++ b/ccstruct/matrix.h @@ -1,5 +1,5 @@ /* -*-C-*- - ******************************************************************************** + ****************************************************************************** * * File: matrix.h (Formerly matrix.h) * Description: Ratings matrix code. (Used by associator) @@ -25,18 +25,28 @@ #ifndef TESSERACT_CCSTRUCT_MATRIX_H__ #define TESSERACT_CCSTRUCT_MATRIX_H__ -#include "ratngs.h" +#include "kdpair.h" #include "unicharset.h" +class BLOB_CHOICE_LIST; + #define NOT_CLASSIFIED reinterpret_cast(NULL) -// A generic class to store a matrix with entries of type T. +// A generic class to hold a 2-D matrix with entries of type T, but can also +// act as a base class for other implementations, such as a triangular or +// banded matrix. template class GENERIC_2D_ARRAY { public: - // Allocate a piece of memory to hold a 2d-array of the given dimension. - // Initialize all the elements of the array to empty instead of assuming - // that a default constructor can be used. + // Initializes the array size, and empty element, but cannot allocate memory + // for the subclasses or initialize because calls to the num_elements + // member will be routed to the base class implementation. Subclasses can + // either pass the memory in, or allocate after by calling Resize(). + GENERIC_2D_ARRAY(int dim1, int dim2, const T& empty, T* array) + : empty_(empty), dim1_(dim1), dim2_(dim2), array_(array) { + } + // Original constructor for a full rectangular matrix DOES allocate memory + // and initialize it to empty. GENERIC_2D_ARRAY(int dim1, int dim2, const T& empty) : empty_(empty), dim1_(dim1), dim2_(dim2) { array_ = new T[dim1_ * dim2_]; @@ -44,26 +54,67 @@ class GENERIC_2D_ARRAY { for (int y = 0; y < dim2_; y++) this->put(x, y, empty_); } - ~GENERIC_2D_ARRAY() { delete[] array_; } + virtual ~GENERIC_2D_ARRAY() { delete[] array_; } + + // Reallocate the array to the given size. Does not keep old data. + void Resize(int size1, int size2, const T& empty) { + empty_ = empty; + if (size1 != dim1_ || size2 != dim2_) { + dim1_ = size1; + dim2_ = size2; + delete [] array_; + array_ = new T[dim1_ * dim2_]; + } + Clear(); + } + + // Reallocate the array to the given size, keeping old data. + void ResizeWithCopy(int size1, int size2) { + if (size1 != dim1_ || size2 != dim2_) { + T* new_array = new T[size1 * size2]; + for (int col = 0; col < size1; ++col) { + for (int row = 0; row < size2; ++row) { + int old_index = col * dim2() + row; + int new_index = col * size2 + row; + if (col < dim1_ && row < dim2_) { + new_array[new_index] = array_[old_index]; + } else { + new_array[new_index] = empty_; + } + } + } + delete[] array_; + array_ = new_array; + dim1_ = size1; + dim2_ = size2; + } + } + + // Sets all the elements of the array to the empty value. + void Clear() { + int total_size = num_elements(); + for (int i = 0; i < total_size; ++i) + array_[i] = empty_; + } // Writes to the given file. Returns false in case of error. // Only works with bitwise-serializeable types! bool Serialize(FILE* fp) const { if (!SerializeSize(fp)) return false; if (fwrite(&empty_, sizeof(empty_), 1, fp) != 1) return false; - int size = dim1_ * dim2_; + int size = num_elements(); if (fwrite(array_, sizeof(*array_), size, fp) != size) return false; return true; } // Reads from the given file. Returns false in case of error. - // Only works with bitwise-serializeable types! + // Only works with bitwise-serializeable typ // If swap is true, assumes a big/little-endian swap is needed. bool DeSerialize(bool swap, FILE* fp) { if (!DeSerializeSize(swap, fp)) return false; if (fread(&empty_, sizeof(empty_), 1, fp) != 1) return false; if (swap) ReverseN(&empty_, sizeof(empty_)); - int size = dim1_ * dim2_; + int size = num_elements(); if (fread(array_, sizeof(*array_), size, fp) != size) return false; if (swap) { for (int i = 0; i < size; ++i) @@ -77,7 +128,7 @@ class GENERIC_2D_ARRAY { bool SerializeClasses(FILE* fp) const { if (!SerializeSize(fp)) return false; if (!empty_.Serialize(fp)) return false; - int size = dim1_ * dim2_; + int size = num_elements(); for (int i = 0; i < size; ++i) { if (!array_[i].Serialize(fp)) return false; } @@ -90,7 +141,7 @@ class GENERIC_2D_ARRAY { bool DeSerializeClasses(bool swap, FILE* fp) { if (!DeSerializeSize(swap, fp)) return false; if (!empty_.DeSerialize(swap, fp)) return false; - int size = dim1_ * dim2_; + int size = num_elements(); for (int i = 0; i < size; ++i) { if (!array_[i].DeSerialize(swap, fp)) return false; } @@ -100,11 +151,14 @@ class GENERIC_2D_ARRAY { // Provide the dimensions of this rectangular matrix. int dim1() const { return dim1_; } int dim2() const { return dim2_; } + // Returns the number of elements in the array. + // Banded/triangular matrices may override. + virtual int num_elements() const { return dim1_ * dim2_; } // Expression to select a specific location in the matrix. The matrix is // stored COLUMN-major, so the left-most index is the most significant. // This allows [][] access to use indices in the same order as (,). - int index(int column, int row) const { + virtual int index(int column, int row) const { return (column * dim2_ + row); } @@ -129,19 +183,21 @@ class GENERIC_2D_ARRAY { T* operator[](int column) { return &array_[this->index(column, 0)]; } + const T* operator[](int column) const { + return &array_[this->index(column, 0)]; + } // Delete objects pointed to by array_[i]. void delete_matrix_pointers() { - for (int x = 0; x < dim1_; x++) { - for (int y = 0; y < dim2_; y++) { - T matrix_cell = this->get(x, y); - if (matrix_cell != empty_) - delete matrix_cell; - } + int size = num_elements(); + for (int i = 0; i < size; ++i) { + T matrix_cell = array_[i]; + if (matrix_cell != empty_) + delete matrix_cell; } } - private: + protected: // Factored helper to serialize the size. bool SerializeSize(FILE* fp) const { inT32 size = dim1_; @@ -160,12 +216,7 @@ class GENERIC_2D_ARRAY { ReverseN(&size1, sizeof(size1)); ReverseN(&size2, sizeof(size2)); } - if (size1 != dim1_ || size2 != dim2_) { - dim1_ = size1; - dim2_ = size2; - delete [] array_; - array_ = new T[dim1_ * dim2_]; - } + Resize(size1, size2, empty_); return true; } @@ -175,25 +226,90 @@ class GENERIC_2D_ARRAY { int dim2_; // Size of the 2nd dimension in indexing functions. }; -// A generic class to store a square matrix with entries of type T. +// A generic class to store a banded triangular matrix with entries of type T. +// In this array, the nominally square matrix is dim1_ x dim1_, and dim2_ is +// the number of bands, INCLUDING the diagonal. The storage is thus of size +// dim1_ * dim2_ and index(col, row) = col * dim2_ + row - col, and an +// assert will fail if row < col or row - col >= dim2. template -class GENERIC_MATRIX : public GENERIC_2D_ARRAY { +class BandTriMatrix : public GENERIC_2D_ARRAY { public: - // Allocate a piece of memory to hold a matrix of the given dimension. - // Initialize all the elements of the matrix to empty instead of assuming + // Allocate a piece of memory to hold a 2d-array of the given dimension. + // Initialize all the elements of the array to empty instead of assuming // that a default constructor can be used. - GENERIC_MATRIX(int dimension, const T& empty) - : GENERIC_2D_ARRAY(dimension, dimension, empty) { + BandTriMatrix(int dim1, int dim2, const T& empty) + : GENERIC_2D_ARRAY(dim1, dim2, empty) { + } + // The default destructor will do. + + // Provide the dimensions of this matrix. + // dimension is the size of the nominally square matrix. + int dimension() const { return this->dim1_; } + // bandwidth is the number of bands in the matrix, INCLUDING the diagonal. + int bandwidth() const { return this->dim2_; } + + // Expression to select a specific location in the matrix. The matrix is + // stored COLUMN-major, so the left-most index is the most significant. + // This allows [][] access to use indices in the same order as (,). + virtual int index(int column, int row) const { + ASSERT_HOST(row >= column); + ASSERT_HOST(row - column < this->dim2_); + return column * this->dim2_ + row - column; } - // Provide the dimension of this square matrix. - int dimension() const { return this->dim1(); } + // Appends array2 corner-to-corner to *this, making an array of dimension + // equal to the sum of the individual dimensions. + // array2 is not destroyed, but is left empty, as all elements are moved + // to *this. + void AttachOnCorner(BandTriMatrix* array2) { + int new_dim1 = this->dim1_ + array2->dim1_; + int new_dim2 = MAX(this->dim2_, array2->dim2_); + T* new_array = new T[new_dim1 * new_dim2]; + for (int col = 0; col < new_dim1; ++col) { + for (int j = 0; j < new_dim2; ++j) { + int new_index = col * new_dim2 + j; + if (col < this->dim1_ && j < this->dim2_) { + new_array[new_index] = this->get(col, col + j); + } else if (col >= this->dim1_ && j < array2->dim2_) { + new_array[new_index] = array2->get(col - this->dim1_, + col - this->dim1_ + j); + array2->put(col - this->dim1_, col - this->dim1_ + j, NULL); + } else { + new_array[new_index] = this->empty_; + } + } + } + delete[] this->array_; + this->array_ = new_array; + this->dim1_ = new_dim1; + this->dim2_ = new_dim2; + } }; -class MATRIX : public GENERIC_MATRIX { +class MATRIX : public BandTriMatrix { public: - MATRIX(int dimension) : GENERIC_MATRIX(dimension, - NOT_CLASSIFIED) {} + MATRIX(int dimension, int bandwidth) + : BandTriMatrix(dimension, bandwidth, NOT_CLASSIFIED) {} + + // Returns true if there are any real classification results. + bool Classified(int col, int row, int wildcard_id) const; + + // Expands the existing matrix in-place to make the band wider, without + // losing any existing data. + void IncreaseBandSize(int bandwidth); + + // Returns a bigger MATRIX with a new column and row in the matrix in order + // to split the blob at the given (ind,ind) diagonal location. + // Entries are relocated to the new MATRIX using the transformation defined + // by MATRIX_COORD::MapForSplit. + // Transfers the pointer data to the new MATRIX and deletes *this. + MATRIX* ConsumeAndMakeBigger(int ind); + + // Makes and returns a deep copy of *this, including all the BLOB_CHOICEs + // on the lists, but not any LanguageModelState that may be attached to the + // BLOB_CHOICEs. + MATRIX* DeepCopy() const; + // Print a shortened version of the contents of the matrix. void print(const UNICHARSET &unicharset) const; }; @@ -203,14 +319,34 @@ struct MATRIX_COORD { MATRIX_COORD *c = static_cast(arg); delete c; } + // Default constructor required by GenericHeap. + MATRIX_COORD() : col(0), row(0) {} MATRIX_COORD(int c, int r): col(c), row(r) {} ~MATRIX_COORD() {} + bool Valid(const MATRIX &m) const { - return (col >= 0 && row >= 0 && - col < m.dimension() && row < m.dimension()); + return 0 <= col && col < m.dimension() && + col <= row && row < col + m.bandwidth() && row < m.dimension(); + } + + // Remaps the col,row pair to split the blob at the given (ind,ind) diagonal + // location. + // Entries at (i,j) for i in [0,ind] and j in [ind,dim) move to (i,j+1), + // making a new row at ind. + // Entries at (i,j) for i in [ind+1,dim) and j in [i,dim) move to (i+i,j+1), + // making a new column at ind+1. + void MapForSplit(int ind) { + ASSERT_HOST(row >= col); + if (col > ind) ++col; + if (row >= ind) ++row; + ASSERT_HOST(row >= col); } + int col; int row; }; +// The MatrixCoordPair contains a MATRIX_COORD and its priority. +typedef tesseract::KDPairInc MatrixCoordPair; + #endif // TESSERACT_CCSTRUCT_MATRIX_H__ diff --git a/ccstruct/ocrblock.cpp b/ccstruct/ocrblock.cpp index 855b31d49b..a328e03887 100644 --- a/ccstruct/ocrblock.cpp +++ b/ccstruct/ocrblock.cpp @@ -472,6 +472,8 @@ void RefreshWordBlobsFromNewBlobs(BLOCK_LIST* block_list, BLOCK_IT block_it(block_list); for (block_it.mark_cycle_pt(); !block_it.cycled_list(); block_it.forward()) { BLOCK* block = block_it.data(); + if (block->poly_block() != NULL && !block->poly_block()->IsText()) + continue; // Don't touch non-text blocks. // Iterate over all rows in the block. ROW_IT row_it(block->row_list()); for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) { diff --git a/ccstruct/pageres.cpp b/ccstruct/pageres.cpp index 9307fef448..c710ee2078 100644 --- a/ccstruct/pageres.cpp +++ b/ccstruct/pageres.cpp @@ -20,68 +20,27 @@ #ifdef __UNIX__ #include #endif +#include "blamer.h" #include "pageres.h" #include "blobs.h" -const char kBlameCorrect[] = "corr"; -const char kBlameClassifier[] = "cl"; -const char kBlameChopper[] = "chop"; -const char kBlameClassLMTradeoff[] = "cl/LM"; -const char kBlamePageLayout[] = "pglt"; -const char kBlameSegsearchHeur[] = "ss_heur"; -const char kBlameSegsearchPP[] = "ss_pp"; -const char kBlameClassOldLMTradeoff[] = "cl/old_LM"; -const char kBlameAdaption[] = "adapt"; -const char kBlameNoTruthSplit[] = "no_tr_spl"; -const char kBlameNoTruth[] = "no_tr"; -const char kBlameUnknown[] = "unkn"; - -const char * const kIncorrectResultReasonNames[] = { - kBlameCorrect, - kBlameClassifier, - kBlameChopper, - kBlameClassLMTradeoff, - kBlamePageLayout, - kBlameSegsearchHeur, - kBlameSegsearchPP, - kBlameClassOldLMTradeoff, - kBlameAdaption, - kBlameNoTruthSplit, - kBlameNoTruth, - kBlameUnknown -}; - -const char *BlamerBundle::IncorrectReasonName(IncorrectResultReason irr) { - return kIncorrectResultReasonNames[irr]; -} - -const char *BlamerBundle::IncorrectReason() const { - return kIncorrectResultReasonNames[incorrect_result_reason]; -} - -void BlamerBundle::FillDebugString(const STRING &msg, - const WERD_CHOICE *choice, - STRING *debug) { - (*debug) += "Truth "; - for (int i = 0; i < this->truth_text.length(); ++i) { - (*debug) += this->truth_text[i]; - } - if (!this->truth_has_char_boxes) (*debug) += " (no char boxes)"; - if (choice != NULL) { - (*debug) += " Choice "; - STRING choice_str; - choice->string_and_lengths(&choice_str, NULL); - (*debug) += choice_str; - } - if (msg.length() > 0) { - (*debug) += "\n"; - (*debug) += msg; - } - (*debug) += "\n"; -} - ELISTIZE (BLOCK_RES) CLISTIZE (BLOCK_RES) ELISTIZE (ROW_RES) ELISTIZE (WERD_RES) + +// Gain factor for computing thresholds that determine the ambiguity of a word. +static const double kStopperAmbiguityThresholdGain = 8.0; +// Constant offset for computing thresholds that determine the ambiguity of a +// word. +static const double kStopperAmbiguityThresholdOffset = 1.5; + +// Computes and returns a threshold of certainty difference used to determine +// which words to keep, based on the adjustment factors of the two words. +// TODO(rays) This is horrible. Replace with an enhance params training model. +static double StopperAmbigThreshold(double f1, double f2) { + return (f2 - f1) * kStopperAmbiguityThresholdGain - + kStopperAmbiguityThresholdOffset; +} + /************************************************************************* * PAGE_RES::PAGE_RES * @@ -189,30 +148,40 @@ WERD_RES& WERD_RES::operator=(const WERD_RES & source) { if (source.rebuild_word != NULL) rebuild_word = new TWERD(*source.rebuild_word); // TODO(rays) Do we ever need to copy the seam_array? + blob_row = source.blob_row; denorm = source.denorm; if (source.box_word != NULL) box_word = new tesseract::BoxWord(*source.box_word); best_state = source.best_state; correct_text = source.correct_text; - - if (source.best_choice != NULL) { - best_choice = new WERD_CHOICE(*source.best_choice); - raw_choice = new WERD_CHOICE(*source.raw_choice); - best_choice_fontinfo_ids = source.best_choice_fontinfo_ids; + blob_widths = source.blob_widths; + blob_gaps = source.blob_gaps; + // None of the uses of operator= require the ratings matrix to be copied, + // so don't as it would be really slow. + + // Copy the cooked choices. + WERD_CHOICE_IT wc_it(const_cast(&source.best_choices)); + WERD_CHOICE_IT wc_dest_it(&best_choices); + for (wc_it.mark_cycle_pt(); !wc_it.cycled_list(); wc_it.forward()) { + const WERD_CHOICE *choice = wc_it.data(); + wc_dest_it.add_after_then_move(new WERD_CHOICE(*choice)); } - else { + if (!wc_dest_it.empty()) { + wc_dest_it.move_to_first(); + best_choice = wc_dest_it.data(); + best_choice_fontinfo_ids = source.best_choice_fontinfo_ids; + } else { best_choice = NULL; - raw_choice = NULL; if (!best_choice_fontinfo_ids.empty()) { best_choice_fontinfo_ids.clear(); } } - for (int i = 0; i < source.alt_choices.length(); ++i) { - const WERD_CHOICE *choice = source.alt_choices[i]; - ASSERT_HOST(choice != NULL); - alt_choices.push_back(new WERD_CHOICE(*choice)); + + if (source.raw_choice != NULL) { + raw_choice = new WERD_CHOICE(*source.raw_choice); + } else { + raw_choice = NULL; } - alt_states = source.alt_states; if (source.ep_choice != NULL) { ep_choice = new WERD_CHOICE(*source.ep_choice); } else { @@ -265,13 +234,23 @@ void WERD_RES::InitForRetryRecognition(const WERD_RES& source) { } } -// Sets up the members used in recognition: -// bln_boxes, chopped_word, seam_array, denorm, best_choice, raw_choice. +// Sets up the members used in recognition: bln_boxes, chopped_word, +// seam_array, denorm. Returns false if +// the word is empty and sets up fake results. If use_body_size is +// true and row->body_size is set, then body_size will be used for +// blob normalization instead of xheight + ascrise. This flag is for +// those languages that are using CJK pitch model and thus it has to +// be true if and only if tesseract->textord_use_cjk_fp_model is +// true. +// If allow_detailed_fx is true, the feature extractor will receive fine +// precision outline information, allowing smoother features and better +// features on low resolution images. // Returns false if the word is empty and sets up fake results. bool WERD_RES::SetupForTessRecognition(const UNICHARSET& unicharset_in, tesseract::Tesseract* tess, Pix* pix, bool numeric_mode, bool use_body_size, + bool allow_detailed_fx, ROW *row, BLOCK* block) { tesseract = tess; POLY_BLOCK* pb = block != NULL ? block->poly_block() : NULL; @@ -284,32 +263,27 @@ bool WERD_RES::SetupForTessRecognition(const UNICHARSET& unicharset_in, } ClearResults(); SetupWordScript(unicharset_in); - chopped_word = TWERD::PolygonalCopy(word); - if (use_body_size && row->body_size() > 0.0f) { - chopped_word->SetupBLNormalize(block, row, row->body_size(), - numeric_mode, &denorm); - } else { - chopped_word->SetupBLNormalize(block, row, x_height, numeric_mode, &denorm); - } - // The image will be 8-bit grey if the input was grey or color. Note that in - // a grey image 0 is black and 255 is white. If the input was binary, then - // the pix will be binary and 0 is white, with 1 being black. - // To tell the difference pixGetDepth() will return 8 or 1. - denorm.set_pix(pix); - // The inverse flag will be true iff the word has been determined to be white - // on black, and is independent of whether the pix is 8 bit or 1 bit. - denorm.set_inverse(word->flag(W_INVERSE)); - chopped_word->Normalize(denorm); - bln_boxes = tesseract::BoxWord::CopyFromNormalized(NULL, chopped_word); - seam_array = start_seam_list(chopped_word->blobs); - best_choice = new WERD_CHOICE(&unicharset_in); - best_choice->make_bad(); - raw_choice = new WERD_CHOICE(&unicharset_in); - raw_choice->make_bad(); + chopped_word = TWERD::PolygonalCopy(allow_detailed_fx, word); + float word_xheight = use_body_size && row->body_size() > 0.0f + ? row->body_size() : x_height; + chopped_word->BLNormalize(block, row, pix, word->flag(W_INVERSE), + word_xheight, numeric_mode, &denorm); + blob_row = row; + SetupBasicsFromChoppedWord(unicharset_in); SetupBlamerBundle(); return true; } +// Set up the seam array, bln_boxes, best_choice, and raw_choice to empty +// accumulators from a made chopped word. We presume the fields are already +// empty. +void WERD_RES::SetupBasicsFromChoppedWord(const UNICHARSET &unicharset_in) { + bln_boxes = tesseract::BoxWord::CopyFromNormalized(chopped_word); + start_seam_list(chopped_word, &seam_array); + SetupBlobWidthsAndGaps(); + ClearWordChoices(); +} + // Sets up the members used in recognition: // bln_boxes, chopped_word, seam_array, denorm, best_choice, raw_choice. // Returns false if the word is empty and sets up fake results. @@ -327,7 +301,7 @@ bool WERD_RES::SetupForCubeRecognition(const UNICHARSET& unicharset_in, ClearResults(); SetupWordScript(unicharset_in); TBOX word_box = word->bounding_box(); - denorm.SetupNormalization(block, NULL, NULL, NULL, NULL, 0, + denorm.SetupNormalization(block, NULL, NULL, word_box.left(), word_box.bottom(), 1.0f, 1.0f, 0.0f, 0.0f); SetupBlamerBundle(); @@ -344,10 +318,6 @@ void WERD_RES::SetupFake(const UNICHARSET& unicharset_in) { bln_boxes = new tesseract::BoxWord; box_word = new tesseract::BoxWord; int blob_count = word->cblob_list()->length(); - best_choice = new WERD_CHOICE("", NULL, 10.0f, -1.0f, - TOP_CHOICE_PERM, unicharset_in); - raw_choice = new WERD_CHOICE("", NULL, 10.0f, -1.0f, - TOP_CHOICE_PERM, unicharset_in); if (blob_count > 0) { BLOB_CHOICE** fake_choices = new BLOB_CHOICE*[blob_count]; // For non-text blocks, just pass any blobs through to the box_word @@ -358,10 +328,16 @@ void WERD_RES::SetupFake(const UNICHARSET& unicharset_in) { TBOX box = b_it.data()->bounding_box(); box_word->InsertBox(box_word->length(), box); fake_choices[blob_id++] = new BLOB_CHOICE(0, 10.0f, -1.0f, - -1, -1, -1, 0, 0, false); + -1, -1, -1, 0, 0, 0, BCC_FAKE); } FakeClassifyWord(blob_count, fake_choices); delete [] fake_choices; + } else { + WERD_CHOICE* word = new WERD_CHOICE(&unicharset_in); + word->make_bad(); + LogNewRawChoice(word); + // Ownership of word is taken by *this WERD_RES in LogNewCookedChoice. + LogNewCookedChoice(1, false, word); } tess_failed = true; } @@ -377,26 +353,309 @@ void WERD_RES::SetupWordScript(const UNICHARSET& uch) { // Sets up the blamer_bundle if it is not null, using the initialized denorm. void WERD_RES::SetupBlamerBundle() { if (blamer_bundle != NULL) { - blamer_bundle->norm_box_tolerance = kBlamerBoxTolerance * denorm.x_scale(); - TPOINT topleft; - TPOINT botright; - TPOINT norm_topleft; - TPOINT norm_botright; - for (int b = 0; b < blamer_bundle->truth_word.length(); ++b) { - const TBOX &box = blamer_bundle->truth_word.BlobBox(b); - topleft.x = box.left(); - topleft.y = box.top(); - botright.x = box.right(); - botright.y = box.bottom(); - denorm.NormTransform(topleft, &norm_topleft); - denorm.NormTransform(botright, &norm_botright); - TBOX norm_box(norm_topleft.x, norm_botright.y, - norm_botright.x, norm_topleft.y); - blamer_bundle->norm_truth_word.InsertBox(b, norm_box); + blamer_bundle->SetupNormTruthWord(denorm); + } +} + +// Computes the blob_widths and blob_gaps from the chopped_word. +void WERD_RES::SetupBlobWidthsAndGaps() { + blob_widths.truncate(0); + blob_gaps.truncate(0); + int num_blobs = chopped_word->NumBlobs(); + for (int b = 0; b < num_blobs; ++b) { + TBLOB *blob = chopped_word->blobs[b]; + TBOX box = blob->bounding_box(); + blob_widths.push_back(box.width()); + if (b + 1 < num_blobs) { + blob_gaps.push_back( + chopped_word->blobs[b + 1]->bounding_box().left() - box.right()); + } + } +} + +// Updates internal data to account for a new SEAM (chop) at the given +// blob_number. Fixes the ratings matrix and states in the choices, as well +// as the blob widths and gaps. +void WERD_RES::InsertSeam(int blob_number, SEAM* seam) { + // Insert the seam into the SEAMS array. + insert_seam(chopped_word, blob_number, seam, &seam_array); + if (ratings != NULL) { + // Expand the ratings matrix. + ratings = ratings->ConsumeAndMakeBigger(blob_number); + // Fix all the segmentation states. + if (raw_choice != NULL) + raw_choice->UpdateStateForSplit(blob_number); + WERD_CHOICE_IT wc_it(&best_choices); + for (wc_it.mark_cycle_pt(); !wc_it.cycled_list(); wc_it.forward()) { + WERD_CHOICE* choice = wc_it.data(); + choice->UpdateStateForSplit(blob_number); + } + SetupBlobWidthsAndGaps(); + } +} + +// Returns true if all the word choices except the first have adjust_factors +// worse than the given threshold. +bool WERD_RES::AlternativeChoiceAdjustmentsWorseThan(float threshold) const { + // The choices are not changed by this iteration. + WERD_CHOICE_IT wc_it(const_cast(&best_choices)); + for (wc_it.forward(); !wc_it.at_first(); wc_it.forward()) { + WERD_CHOICE* choice = wc_it.data(); + if (choice->adjust_factor() <= threshold) + return false; + } + return true; +} + +// Returns true if the current word is ambiguous (by number of answers or +// by dangerous ambigs.) +bool WERD_RES::IsAmbiguous() { + return !best_choices.singleton() || best_choice->dangerous_ambig_found(); +} + +// Returns true if the ratings matrix size matches the sum of each of the +// segmentation states. +bool WERD_RES::StatesAllValid() { + int ratings_dim = ratings->dimension(); + if (raw_choice->TotalOfStates() != ratings_dim) { + tprintf("raw_choice has total of states = %d vs ratings dim of %d\n", + raw_choice->TotalOfStates(), ratings_dim); + return false; + } + WERD_CHOICE_IT it(&best_choices); + int index = 0; + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward(), ++index) { + WERD_CHOICE* choice = it.data(); + if (choice->TotalOfStates() != ratings_dim) { + tprintf("Cooked #%d has total of states = %d vs ratings dim of %d\n", + choice->TotalOfStates(), ratings_dim); + return false; + } + } + return true; +} + +// Prints a list of words found if debug is true or the word result matches +// the word_to_debug. +void WERD_RES::DebugWordChoices(bool debug, const char* word_to_debug) { + if (debug || + (word_to_debug != NULL && *word_to_debug != '\0' && best_choice != NULL && + best_choice->unichar_string() == STRING(word_to_debug))) { + if (raw_choice != NULL) + raw_choice->print("\nBest Raw Choice"); + + WERD_CHOICE_IT it(&best_choices); + int index = 0; + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward(), ++index) { + WERD_CHOICE* choice = it.data(); + STRING label; + label.add_str_int("\nCooked Choice #", index); + choice->print(label.string()); } } } +// Removes from best_choices all choices which are not within a reasonable +// range of the best choice. +// TODO(rays) incorporate the information used here into the params training +// re-ranker, in place of this heuristic that is based on the previous +// adjustment factor. +void WERD_RES::FilterWordChoices(int debug_level) { + if (best_choice == NULL || best_choices.singleton()) + return; + + if (debug_level >= 2) + best_choice->print("\nFiltering against best choice"); + WERD_CHOICE_IT it(&best_choices); + int index = 0; + for (it.forward(); !it.at_first(); it.forward(), ++index) { + WERD_CHOICE* choice = it.data(); + float threshold = StopperAmbigThreshold(best_choice->adjust_factor(), + choice->adjust_factor()); + // i, j index the blob choice in choice, best_choice. + // chunk is an index into the chopped_word blobs (AKA chunks). + // Since the two words may use different segmentations of the chunks, we + // iterate over the chunks to find out whether a comparable blob + // classification is much worse than the best result. + int i = 0, j = 0, chunk = 0; + // Each iteration of the while deals with 1 chunk. On entry choice_chunk + // and best_chunk are the indices of the first chunk in the NEXT blob, + // i.e. we don't have to increment i, j while chunk < choice_chunk and + // best_chunk respectively. + int choice_chunk = choice->state(0), best_chunk = best_choice->state(0); + while (i < choice->length() && j < best_choice->length()) { + if (choice->unichar_id(i) != best_choice->unichar_id(j) && + choice->certainty(i) - best_choice->certainty(j) < threshold) { + if (debug_level >= 2) { + STRING label; + label.add_str_int("\nDiscarding bad choice #", index); + choice->print(label.string()); + tprintf("i %d j %d Chunk %d Choice->Blob[i].Certainty %.4g" + " BestChoice->ChunkCertainty[Chunk] %g Threshold %g\n", + i, j, chunk, choice->certainty(i), + best_choice->certainty(j), threshold); + } + delete it.extract(); + break; + } + ++chunk; + // If needed, advance choice_chunk to keep up with chunk. + while (choice_chunk < chunk && ++i < choice->length()) + choice_chunk += choice->state(i); + // If needed, advance best_chunk to keep up with chunk. + while (best_chunk < chunk && ++j < best_choice->length()) + best_chunk += best_choice->state(j); + } + } +} + +void WERD_RES::ComputeAdaptionThresholds(float certainty_scale, + float min_rating, + float max_rating, + float rating_margin, + float* thresholds) { + int chunk = 0; + int end_chunk = best_choice->state(0); + int end_raw_chunk = raw_choice->state(0); + int raw_blob = 0; + for (int i = 0; i < best_choice->length(); i++, thresholds++) { + float avg_rating = 0.0f; + int num_error_chunks = 0; + + // For each chunk in best choice blob i, count non-matching raw results. + while (chunk < end_chunk) { + if (chunk >= end_raw_chunk) { + ++raw_blob; + end_raw_chunk += raw_choice->state(raw_blob); + } + if (best_choice->unichar_id(i) != + raw_choice->unichar_id(raw_blob)) { + avg_rating += raw_choice->certainty(raw_blob); + ++num_error_chunks; + } + ++chunk; + } + + if (num_error_chunks > 0) { + avg_rating /= num_error_chunks; + *thresholds = (avg_rating / -certainty_scale) * (1.0 - rating_margin); + } else { + *thresholds = max_rating; + } + + if (*thresholds > max_rating) + *thresholds = max_rating; + if (*thresholds < min_rating) + *thresholds = min_rating; + } +} + +// Saves a copy of the word_choice if it has the best unadjusted rating. +// Returns true if the word_choice was the new best. +bool WERD_RES::LogNewRawChoice(WERD_CHOICE* word_choice) { + if (raw_choice == NULL || word_choice->rating() < raw_choice->rating()) { + delete raw_choice; + raw_choice = new WERD_CHOICE(*word_choice); + raw_choice->set_permuter(TOP_CHOICE_PERM); + return true; + } + return false; +} + +// Consumes word_choice by adding it to best_choices, (taking ownership) if +// the certainty for word_choice is some distance of the best choice in +// best_choices, or by deleting the word_choice and returning false. +// The best_choices list is kept in sorted order by rating. Duplicates are +// removed, and the list is kept no longer than max_num_choices in length. +// Returns true if the word_choice is still a valid pointer. +bool WERD_RES::LogNewCookedChoice(int max_num_choices, bool debug, + WERD_CHOICE* word_choice) { + if (best_choice != NULL) { + // Throw out obviously bad choices to save some work. + // TODO(rays) Get rid of this! This piece of code produces different + // results according to the order in which words are found, which is an + // undesirable behavior. It would be better to keep all the choices and + // prune them later when more information is available. + float max_certainty_delta = + StopperAmbigThreshold(best_choice->adjust_factor(), + word_choice->adjust_factor()); + if (max_certainty_delta > -kStopperAmbiguityThresholdOffset) + max_certainty_delta = -kStopperAmbiguityThresholdOffset; + if (word_choice->certainty() - best_choice->certainty() < + max_certainty_delta) { + if (debug) { + STRING bad_string; + word_choice->string_and_lengths(&bad_string, NULL); + tprintf("Discarding choice \"%s\" with an overly low certainty" + " %.3f vs best choice certainty %.3f (Threshold: %.3f)\n", + bad_string.string(), word_choice->certainty(), + best_choice->certainty(), + max_certainty_delta + best_choice->certainty()); + } + delete word_choice; + return false; + } + } + + // Insert in the list in order of increasing rating, but knock out worse + // string duplicates. + WERD_CHOICE_IT it(&best_choices); + const STRING& new_str = word_choice->unichar_string(); + bool inserted = false; + int num_choices = 0; + if (!it.empty()) { + do { + WERD_CHOICE* choice = it.data(); + if (choice->rating() > word_choice->rating() && !inserted) { + // Time to insert. + it.add_before_stay_put(word_choice); + inserted = true; + if (num_choices == 0) + best_choice = word_choice; // This is the new best. + ++num_choices; + } + if (choice->unichar_string() == new_str) { + if (inserted) { + // New is better. + delete it.extract(); + } else { + // Old is better. + if (debug) { + tprintf("Discarding duplicate choice \"%s\", rating %g vs %g\n", + new_str.string(), word_choice->rating(), choice->rating()); + } + delete word_choice; + return false; + } + } else { + ++num_choices; + if (num_choices > max_num_choices) + delete it.extract(); + } + it.forward(); + } while (!it.at_first()); + } + if (!inserted && num_choices < max_num_choices) { + it.add_to_end(word_choice); + inserted = true; + if (num_choices == 0) + best_choice = word_choice; // This is the new best. + } + if (debug) { + if (inserted) + tprintf("New %s", best_choice == word_choice ? "Best" : "Secondary"); + else + tprintf("Poor"); + word_choice->print(" Word Choice"); + } + if (!inserted) { + delete word_choice; + return false; + } + return true; +} + + // Simple helper moves the ownership of the pointer data from src to dest, // first deleting anything in dest, and nulling out src afterwards. template static void MovePointerData(T** dest, T**src) { @@ -405,24 +664,75 @@ template static void MovePointerData(T** dest, T**src) { *src = NULL; } +// Prints a brief list of all the best choices. +void WERD_RES::PrintBestChoices() const { + STRING alternates_str; + WERD_CHOICE_IT it(const_cast(&best_choices)); + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { + if (!it.at_first()) alternates_str += "\", \""; + alternates_str += it.data()->unichar_string(); + } + tprintf("Alternates for \"%s\": {\"%s\"}\n", + best_choice->unichar_string().string(), alternates_str.string()); +} + +// Returns the sum of the widths of the blob between start_blob and last_blob +// inclusive. +int WERD_RES::GetBlobsWidth(int start_blob, int last_blob) { + int result = 0; + for (int b = start_blob; b <= last_blob; ++b) { + result += blob_widths[b]; + if (b < last_blob) + result += blob_gaps[b]; + } + return result; +} +// Returns the width of a gap between the specified blob and the next one. +int WERD_RES::GetBlobsGap(int blob_index) { + if (blob_index < 0 || blob_index >= blob_gaps.size()) + return 0; + return blob_gaps[blob_index]; +} + +// Returns the BLOB_CHOICE corresponding to the given index in the +// best choice word taken from the appropriate cell in the ratings MATRIX. +// Borrowed pointer, so do not delete. May return NULL if there is no +// BLOB_CHOICE matching the unichar_id at the given index. +BLOB_CHOICE* WERD_RES::GetBlobChoice(int index) const { + if (index < 0 || index >= best_choice->length()) return NULL; + BLOB_CHOICE_LIST* choices = GetBlobChoices(index); + return FindMatchingChoice(best_choice->unichar_id(index), choices); +} + +// Returns the BLOB_CHOICE_LIST corresponding to the given index in the +// best choice word taken from the appropriate cell in the ratings MATRIX. +// Borrowed pointer, so do not delete. +BLOB_CHOICE_LIST* WERD_RES::GetBlobChoices(int index) const { + return best_choice->blob_choices(index, ratings); +} + // Moves the results fields from word to this. This takes ownership of all // the data, so src can be destructed. void WERD_RES::ConsumeWordResults(WERD_RES* word) { denorm = word->denorm; + blob_row = word->blob_row; MovePointerData(&chopped_word, &word->chopped_word); MovePointerData(&rebuild_word, &word->rebuild_word); MovePointerData(&box_word, &word->box_word); - if (seam_array != NULL) - free_seam_list(seam_array); + seam_array.delete_data_pointers(); seam_array = word->seam_array; - word->seam_array = NULL; + word->seam_array.clear(); best_state.move(&word->best_state); correct_text.move(&word->correct_text); - MovePointerData(&best_choice, &word->best_choice); + blob_widths.move(&word->blob_widths); + blob_gaps.move(&word->blob_gaps); + if (ratings != NULL) ratings->delete_matrix_pointers(); + MovePointerData(&ratings, &word->ratings); + best_choice = word->best_choice; MovePointerData(&raw_choice, &word->raw_choice); - alt_choices.delete_data_pointers(); - alt_choices.move(&word->alt_choices); - alt_states.move(&word->alt_states); + best_choices.clear(); + WERD_CHOICE_IT wc_it(&best_choices); + wc_it.add_list_after(&word->best_choices); reject_map = word->reject_map; if (word->blamer_bundle != NULL) { assert(blamer_bundle != NULL); @@ -432,44 +742,38 @@ void WERD_RES::ConsumeWordResults(WERD_RES* word) { } // Replace the best choice and rebuild box word. -void WERD_RES::ReplaceBestChoice( - const WERD_CHOICE& choice, - const GenericVector& segmentation_state) { - delete best_choice; - best_choice = new WERD_CHOICE(choice); - best_state = segmentation_state; +// choice must be from the current best_choices list. +void WERD_RES::ReplaceBestChoice(WERD_CHOICE* choice) { + best_choice = choice; RebuildBestState(); SetupBoxWord(); // Make up a fake reject map of the right length to keep the // rejection pass happy. - reject_map.initialise(segmentation_state.length()); + reject_map.initialise(best_state.length()); done = tess_accepted = tess_would_adapt = true; SetScriptPositions(); } -// Builds the rebuild_word from the chopped_word and the best_state. +// Builds the rebuild_word and sets the best_state from the chopped_word and +// the best_choice->state. void WERD_RES::RebuildBestState() { + ASSERT_HOST(best_choice != NULL); if (rebuild_word != NULL) delete rebuild_word; rebuild_word = new TWERD; - if (seam_array == NULL) { - seam_array = start_seam_list(chopped_word->blobs); - } - TBLOB* prev_blob = NULL; + if (seam_array.empty()) + start_seam_list(chopped_word, &seam_array); + best_state.truncate(0); int start = 0; - for (int i = 0; i < best_state.size(); ++i) { - int length = best_state[i]; - join_pieces(chopped_word->blobs, seam_array, start, start + length - 1); - TBLOB* blob = chopped_word->blobs; - for (int i = 0; i < start; ++i) - blob = blob->next; - TBLOB* copy_blob = new TBLOB(*blob); - if (prev_blob == NULL) - rebuild_word->blobs = copy_blob; - else - prev_blob->next = copy_blob; - prev_blob = copy_blob; - break_pieces(blob, seam_array, start, start + length - 1); + for (int i = 0; i < best_choice->length(); ++i) { + int length = best_choice->state(i); + best_state.push_back(length); + if (length > 1) + join_pieces(seam_array, start, start + length - 1, chopped_word); + TBLOB* blob = chopped_word->blobs[start]; + rebuild_word->blobs.push_back(new TBLOB(*blob)); + if (length > 1) + break_pieces(seam_array, start, start + length - 1, chopped_word); start += length; } } @@ -495,78 +799,74 @@ void WERD_RES::SetupBoxWord() { if (box_word != NULL) delete box_word; rebuild_word->ComputeBoundingBoxes(); - box_word = tesseract::BoxWord::CopyFromNormalized(&denorm, rebuild_word); + box_word = tesseract::BoxWord::CopyFromNormalized(rebuild_word); box_word->ClipToOriginalWord(denorm.block(), word); } -// Sets up the script positions in the output boxword using the best_choice +// Sets up the script positions in the output best_choice using the best_choice // to get the unichars, and the unicharset to get the target positions. void WERD_RES::SetScriptPositions() { - box_word->SetScriptPositions(*uch_set, small_caps, rebuild_word, - best_choice); -} - -void WERD_RES::WithoutFootnoteSpan(int *pstart, int *pend) const { - int end = best_choice->length(); - while (end > 0 && - uch_set->get_isdigit(best_choice->unichar_ids()[end - 1]) && - box_word->BlobPosition(end - 1) == tesseract::SP_SUPERSCRIPT) { - end--; - } - int start = 0; - while (start < end && - uch_set->get_isdigit(best_choice->unichar_ids()[start]) && - box_word->BlobPosition(start) == tesseract::SP_SUPERSCRIPT) { - start++; - } - *pstart = start; - *pend = end; + best_choice->SetScriptPositions(small_caps, chopped_word); } - -void WERD_RES::WithoutFootnoteSpan( - const WERD_CHOICE &word, const GenericVector &state, - int *pstart, int *pend) const { - int len = word.length(); - *pstart = 0; - *pend = len; - if (len < 2) return; - if (!word.unicharset()->get_isdigit(word.unichar_ids()[len - 1]) && - !word.unicharset()->get_isdigit(word.unichar_ids()[0])) return; - - // ok, now that we know the word ends in digits, do the expensive bit of - // figuring out if they're superscript. - WERD_RES copy(*this); - copy.ReplaceBestChoice(word, state); - copy.WithoutFootnoteSpan(pstart, pend); +// Sets all the blobs in all the words (raw choice and best choices) to be +// the given position. (When a sub/superscript is recognized as a separate +// word, it falls victim to the rule that a whole word cannot be sub or +// superscript, so this function overrides that problem.) +void WERD_RES::SetAllScriptPositions(tesseract::ScriptPos position) { + raw_choice->SetAllScriptPositions(position); + WERD_CHOICE_IT wc_it(&best_choices); + for (wc_it.mark_cycle_pt(); !wc_it.cycled_list(); wc_it.forward()) + wc_it.data()->SetAllScriptPositions(position); } // Classifies the word with some already-calculated BLOB_CHOICEs. // The choices are an array of blob_count pointers to BLOB_CHOICE, // providing a single classifier result for each blob. // The BLOB_CHOICEs are consumed and the word takes ownership. -// The number of blobs in the outword must match blob_count. +// The number of blobs in the box_word must match blob_count. void WERD_RES::FakeClassifyWord(int blob_count, BLOB_CHOICE** choices) { // Setup the WERD_RES. ASSERT_HOST(box_word != NULL); ASSERT_HOST(blob_count == box_word->length()); - ASSERT_HOST(best_choice != NULL); - BLOB_CHOICE_LIST_CLIST* word_choices = new BLOB_CHOICE_LIST_CLIST; - BLOB_CHOICE_LIST_C_IT bc_it(word_choices); + ClearWordChoices(); + ClearRatings(); + ratings = new MATRIX(blob_count, 1); for (int c = 0; c < blob_count; ++c) { - best_choice->append_unichar_id( - choices[c]->unichar_id(), 1, - choices[c]->rating(), choices[c]->certainty()); BLOB_CHOICE_LIST* choice_list = new BLOB_CHOICE_LIST; BLOB_CHOICE_IT choice_it(choice_list); choice_it.add_after_then_move(choices[c]); - bc_it.add_after_then_move(choice_list); + ratings->put(c, c, choice_list); } - best_choice->set_blob_choices(word_choices); - delete raw_choice; - raw_choice = new WERD_CHOICE(*best_choice); + FakeWordFromRatings(); reject_map.initialise(blob_count); } +// Creates a WERD_CHOICE for the word using the top choices from the leading +// diagonal of the ratings matrix. +void WERD_RES::FakeWordFromRatings() { + int num_blobs = ratings->dimension(); + WERD_CHOICE* word_choice = new WERD_CHOICE(uch_set, num_blobs); + word_choice->set_permuter(TOP_CHOICE_PERM); + for (int b = 0; b < num_blobs; ++b) { + UNICHAR_ID unichar_id = UNICHAR_SPACE; + float rating = MAX_INT32; + float certainty = -MAX_INT32; + BLOB_CHOICE_LIST* choices = ratings->get(b, b); + if (choices != NULL && !choices->empty()) { + BLOB_CHOICE_IT bc_it(choices); + BLOB_CHOICE* choice = bc_it.data(); + unichar_id = choice->unichar_id(); + rating = choice->rating(); + certainty = choice->certainty(); + } + word_choice->append_unichar_id_space_allocated(unichar_id, 1, rating, + certainty); + } + LogNewRawChoice(word_choice); + // Ownership of word_choice taken by word here. + LogNewCookedChoice(1, false, word_choice); +} + // Copies the best_choice strings to the correct_text for adaption/training. void WERD_RES::BestChoiceToCorrectText() { correct_text.clear(); @@ -585,9 +885,8 @@ void WERD_RES::BestChoiceToCorrectText() { // Returns true if anything was merged. bool WERD_RES::ConditionalBlobMerge( TessResultCallback2* class_cb, - TessResultCallback2* box_cb, - - BLOB_CHOICE_LIST_CLIST *blob_choices) { + TessResultCallback2* box_cb) { + ASSERT_HOST(best_choice->length() == 0 || ratings != NULL); bool modified = false; for (int i = 0; i + 1 < best_choice->length(); ++i) { UNICHAR_ID new_id = class_cb->Run(best_choice->unichar_id(i), @@ -595,38 +894,22 @@ bool WERD_RES::ConditionalBlobMerge( if (new_id != INVALID_UNICHAR_ID && (box_cb == NULL || box_cb->Run(box_word->BlobBox(i), box_word->BlobBox(i + 1)))) { - if (reject_map.length() == best_choice->length()) - reject_map.remove_pos(i); + // Raw choice should not be fixed. best_choice->set_unichar_id(new_id, i); - best_choice->remove_unichar_id(i + 1); - raw_choice->set_unichar_id(new_id, i); - raw_choice->remove_unichar_id(i + 1); modified = true; - rebuild_word->MergeBlobs(i, i + 2); - box_word->MergeBoxes(i, i + 2); - if (i + 1 < best_state.length()) { - best_state[i] += best_state[i + 1]; - best_state.remove(i + 1); + MergeAdjacentBlobs(i); + const MATRIX_COORD& coord = best_choice->MatrixCoord(i); + if (!coord.Valid(*ratings)) { + ratings->IncreaseBandSize(coord.row + 1 - coord.col); } - - BLOB_CHOICE_LIST_C_IT blob_choices_it(blob_choices); - for (int j = 0; j < i; ++j) - blob_choices_it.forward(); - BLOB_CHOICE_IT it1(blob_choices_it.data()); // first choices - BLOB_CHOICE_LIST* target_choices = blob_choices_it.data_relative(1); - BLOB_CHOICE_IT it2(target_choices); // second choices - float certainty = it2.data()->certainty(); - float rating = it2.data()->rating(); - if (it1.data()->certainty() < certainty) { - certainty = it1.data()->certainty(); - rating = it1.data()->rating(); - target_choices = blob_choices_it.data(); - blob_choices_it.forward(); + BLOB_CHOICE_LIST* blob_choices = GetBlobChoices(i); + if (FindMatchingChoice(new_id, blob_choices) == NULL) { + // Insert a fake result. + BLOB_CHOICE* blob_choice = new BLOB_CHOICE; + blob_choice->set_unichar_id(new_id); + BLOB_CHOICE_IT bc_it(blob_choices); + bc_it.add_before_then_move(blob_choice); } - delete blob_choices_it.extract(); // get rid of spare - // TODO(rays) Fix the choices so they contain the desired result. - // Do we really need to ? Only needed for fix_quotes, which should be - // going away. } } delete class_cb; @@ -634,6 +917,20 @@ bool WERD_RES::ConditionalBlobMerge( return modified; } +// Merges 2 adjacent blobs in the result (index and index+1) and corrects +// all the data to account for the change. +void WERD_RES::MergeAdjacentBlobs(int index) { + if (reject_map.length() == best_choice->length()) + reject_map.remove_pos(index); + best_choice->remove_unichar_id(index + 1); + rebuild_word->MergeBlobs(index, index + 2); + box_word->MergeBoxes(index, index + 2); + if (index + 1 < best_state.length()) { + best_state[index] += best_state[index + 1]; + best_state.remove(index + 1); + } +} + // TODO(tkielbus) Decide between keeping this behavior here or modifying the // training data. @@ -666,15 +963,14 @@ UNICHAR_ID WERD_RES::BothQuotes(UNICHAR_ID id1, UNICHAR_ID id2) { } // Change pairs of quotes to double quotes. -void WERD_RES::fix_quotes(BLOB_CHOICE_LIST_CLIST* blob_choices) { +void WERD_RES::fix_quotes() { if (!uch_set->contains_unichar("\"") || !uch_set->get_enabled(uch_set->unichar_to_id("\""))) return; // Don't create it if it is disallowed. ConditionalBlobMerge( NewPermanentTessCallback(this, &WERD_RES::BothQuotes), - NULL, - blob_choices); + NULL); } // Callback helper for fix_hyphens returns UNICHAR_ID of - if both @@ -696,15 +992,14 @@ bool WERD_RES::HyphenBoxesOverlap(const TBOX& box1, const TBOX& box2) { // Change pairs of hyphens to a single hyphen if the bounding boxes touch // Typically a long dash which has been segmented. -void WERD_RES::fix_hyphens(BLOB_CHOICE_LIST_CLIST *blob_choices) { +void WERD_RES::fix_hyphens() { if (!uch_set->contains_unichar("-") || !uch_set->get_enabled(uch_set->unichar_to_id("-"))) return; // Don't create it if it is disallowed. ConditionalBlobMerge( NewPermanentTessCallback(this, &WERD_RES::BothHyphens), - NewPermanentTessCallback(this, &WERD_RES::HyphenBoxesOverlap), - blob_choices); + NewPermanentTessCallback(this, &WERD_RES::HyphenBoxesOverlap)); } // Callback helper for merge_tess_fails returns a space if both @@ -719,8 +1014,7 @@ UNICHAR_ID WERD_RES::BothSpaces(UNICHAR_ID id1, UNICHAR_ID id2) { // Change pairs of tess failures to a single one void WERD_RES::merge_tess_fails() { if (ConditionalBlobMerge( - NewPermanentTessCallback(this, &WERD_RES::BothSpaces), NULL, - best_choice->blob_choices())) { + NewPermanentTessCallback(this, &WERD_RES::BothSpaces), NULL)) { int len = best_choice->length(); ASSERT_HOST(reject_map.length() == len); ASSERT_HOST(box_word->length() == len); @@ -732,8 +1026,8 @@ void WERD_RES::merge_tess_fails() { bool WERD_RES::PiecesAllNatural(int start, int count) const { // all seams must have no splits. for (int index = start; index < start + count - 1; ++index) { - if (index >= 0 && index < array_count(seam_array)) { - SEAM* seam = reinterpret_cast(array_value(seam_array, index)); + if (index >= 0 && index < seam_array.size()) { + SEAM* seam = seam_array[index]; if (seam != NULL && seam->split1 != NULL) return false; } @@ -774,11 +1068,12 @@ void WERD_RES::InitNonPointers() { void WERD_RES::InitPointers() { word = NULL; bln_boxes = NULL; + blob_row = NULL; uch_set = NULL; chopped_word = NULL; rebuild_word = NULL; box_word = NULL; - seam_array = NULL; + ratings = NULL; best_choice = NULL; raw_choice = NULL; ep_choice = NULL; @@ -805,6 +1100,7 @@ void WERD_RES::ClearResults() { delete bln_boxes; bln_boxes = NULL; } + blob_row = NULL; if (chopped_word != NULL) { delete chopped_word; chopped_word = NULL; @@ -819,27 +1115,34 @@ void WERD_RES::ClearResults() { } best_state.clear(); correct_text.clear(); - if (seam_array != NULL) { - free_seam_list(seam_array); - seam_array = NULL; - } - if (best_choice != NULL) { - delete best_choice; + seam_array.delete_data_pointers(); + seam_array.clear(); + blob_widths.clear(); + blob_gaps.clear(); + ClearRatings(); + ClearWordChoices(); + if (blamer_bundle != NULL) blamer_bundle->ClearResults(); +} +void WERD_RES::ClearWordChoices() { + best_choice = NULL; + if (raw_choice != NULL) { delete raw_choice; - best_choice = NULL; raw_choice = NULL; } - if (!alt_choices.empty()) { - alt_choices.delete_data_pointers(); - alt_choices.clear(); - } - alt_states.clear(); + best_choices.clear(); if (ep_choice != NULL) { delete ep_choice; ep_choice = NULL; } - if (blamer_bundle != NULL) blamer_bundle->ClearResults(); } +void WERD_RES::ClearRatings() { + if (ratings != NULL) { + ratings->delete_matrix_pointers(); + delete ratings; + ratings = NULL; + } +} + bool PAGE_RES_IT::operator ==(const PAGE_RES_IT &other) const { return word_res == other.word_res && diff --git a/ccstruct/pageres.h b/ccstruct/pageres.h index bd53586b50..d8044129ac 100644 --- a/ccstruct/pageres.h +++ b/ccstruct/pageres.h @@ -19,6 +19,7 @@ #ifndef PAGERES_H #define PAGERES_H +#include "blamer.h" #include "blobs.h" #include "boxword.h" #include "elst.h" @@ -38,167 +39,6 @@ class Tesseract; } using tesseract::FontInfo; -static const inT16 kBlamerBoxTolerance = 5; - -// Enum for expressing the source of error. -// Note: Please update kIncorrectResultReasonNames when modifying this enum. -enum IncorrectResultReason { - // The text recorded in best choice == truth text - IRR_CORRECT, - // Either: Top choice is incorrect and is a dictionary word (language model - // is unlikely to help correct such errors, so blame the classifier). - // Or: the correct unichar was not included in shortlist produced by the - // classifier at all. - IRR_CLASSIFIER, - // Chopper have not found one or more splits that correspond to the correct - // character bounding boxes recorded in BlamerBundle::truth_word. - IRR_CHOPPER, - // Classifier did include correct unichars for each blob in the correct - // segmentation, however its rating could have been too bad to allow the - // language model to pull out the correct choice. On the other hand the - // strength of the language model might have been too weak to favor the - // correct answer, this we call this case a classifier-language model - // tradeoff error. - IRR_CLASS_LM_TRADEOFF, - // Page layout failed to produce the correct bounding box. Blame page layout - // if the truth was not found for the word, which implies that the bounding - // box of the word was incorrect (no truth word had a similar bounding box). - IRR_PAGE_LAYOUT, - // SegSearch heuristic prevented one or more blobs from the correct - // segmentation state to be classified (e.g. the blob was too wide). - IRR_SEGSEARCH_HEUR, - // The correct segmentaiton state was not explored because of poor SegSearch - // pain point prioritization. We blame SegSearch pain point prioritization - // if the best rating of a choice constructed from correct segmentation is - // better than that of the best choice (i.e. if we got to explore the correct - // segmentation state, language model would have picked the correct choice). - IRR_SEGSEARCH_PP, - // Same as IRR_CLASS_LM_TRADEOFF, but used when we only run chopper on a word, - - // and thus use the old language model (permuters). - // TODO(antonova): integrate the new language mode with chopper - IRR_CLASS_OLD_LM_TRADEOFF, - // If there is an incorrect adaptive template match with a better score than - // a correct one (either pre-trained or adapted), mark this as adaption error. - IRR_ADAPTION, - // split_and_recog_word() failed to find a suitable split in truth. - IRR_NO_TRUTH_SPLIT, - // Truth is not available for this word (e.g. when words in corrected content - // file are turned into ~~~~ because an appropriate alignment was not found. - IRR_NO_TRUTH, - // The text recorded in best choice != truth text, but none of the above - // reasons are set. - IRR_UNKNOWN, - - IRR_NUM_REASONS -}; - -// Blamer-related information to determine the source of errors. -struct BlamerBundle { - static const char *IncorrectReasonName(IncorrectResultReason irr); - BlamerBundle() : truth_has_char_boxes(false), - incorrect_result_reason(IRR_CORRECT), - lattice_data(NULL) { ClearResults(); } - ~BlamerBundle() { delete[] lattice_data; } - void ClearResults() { - norm_truth_word.DeleteAllBoxes(); - norm_box_tolerance = 0; - if (!NoTruth()) incorrect_result_reason = IRR_CORRECT; - debug = ""; - segsearch_is_looking_for_blame = false; - best_correctly_segmented_rating = WERD_CHOICE::kBadRating; - correct_segmentation_cols.clear(); - correct_segmentation_rows.clear(); - best_choice_is_dict_and_top_choice = false; - delete[] lattice_data; - lattice_data = NULL; - lattice_size = 0; - } - void CopyTruth(const BlamerBundle &other) { - truth_has_char_boxes = other.truth_has_char_boxes; - truth_word = other.truth_word; - truth_text = other.truth_text; - incorrect_result_reason = - (other.NoTruth() ? other.incorrect_result_reason : IRR_CORRECT); - } - void CopyResults(const BlamerBundle &other) { - norm_truth_word = other.norm_truth_word; - norm_box_tolerance = other.norm_box_tolerance; - incorrect_result_reason = other.incorrect_result_reason; - segsearch_is_looking_for_blame = other.segsearch_is_looking_for_blame; - best_correctly_segmented_rating =other.best_correctly_segmented_rating; - correct_segmentation_cols = other.correct_segmentation_cols; - correct_segmentation_rows = other.correct_segmentation_rows; - best_choice_is_dict_and_top_choice = - other.best_choice_is_dict_and_top_choice; - if (other.lattice_data != NULL) { - lattice_data = new char[other.lattice_size]; - memcpy(lattice_data, other.lattice_data, other.lattice_size); - lattice_size = other.lattice_size; - } else { - lattice_data = NULL; - } - } - BlamerBundle(const BlamerBundle &other) { - this->CopyTruth(other); - this->CopyResults(other); - } - const char *IncorrectReason() const; - bool NoTruth() const { - return (incorrect_result_reason == IRR_NO_TRUTH || - incorrect_result_reason == IRR_PAGE_LAYOUT); - } - void SetBlame(IncorrectResultReason irr, - const STRING &msg, const WERD_CHOICE *choice, bool debug) { - this->incorrect_result_reason = irr; - this->debug = this->IncorrectReason(); - this->debug += " to blame: "; - this->FillDebugString(msg, choice, &(this->debug)); - if (debug) tprintf("SetBlame(): %s", this->debug.string()); - } - // Appends choice and truth details to the given debug string. - void FillDebugString(const STRING &msg, const WERD_CHOICE *choice, - STRING *debug); - - // Set to true when bounding boxes for individual unichars are recorded. - bool truth_has_char_boxes; - // The true_word (in the original image coordinate space) contains ground - // truth bounding boxes for this WERD_RES. - tesseract::BoxWord truth_word; - // Same as above, but in normalized coordinates - // (filled in by WERD_RES::SetupForRecognition()). - tesseract::BoxWord norm_truth_word; - // Tolerance for bounding box comparisons in normalized space. - int norm_box_tolerance; - // Contains ground truth unichar for each of the bounding boxes in truth_word. - GenericVector truth_text; - // The reason for incorrect OCR result. - IncorrectResultReason incorrect_result_reason; - // Debug text associated with the blame. - STRING debug; - // Misadaption debug information (filled in if this word was misadapted to). - STRING misadaption_debug; - // Variables used by the segmentation search when looking for the blame. - // Set to true while segmentation search is continued after the usual - // termination condition in order to look for the blame. - bool segsearch_is_looking_for_blame; - // Best rating for correctly segmented path - // (set and used by SegSearch when looking for blame). - float best_correctly_segmented_rating; - // Vectors populated by SegSearch to indicate column and row indices that - // correspond to blobs with correct bounding boxes. - GenericVector correct_segmentation_cols; - GenericVector correct_segmentation_rows; - // Set to true if best choice is a dictionary word and - // classifier's top choice. - bool best_choice_is_dict_and_top_choice; - // Serialized segmentation search lattice. - char *lattice_data; - int lattice_size; // size of lattice_data in bytes - // Information about hypotheses (paths) explored by the segmentation search. - tesseract::ParamsTrainingBundle params_training_bundle; -}; - /* Forward declarations */ class BLOCK_RES; @@ -341,8 +181,11 @@ class WERD_RES : public ELIST_LINK { // TODO(rays) determine if docqual does anything useful and delete bln_boxes // if it doesn't. tesseract::BoxWord* bln_boxes; // BLN input bounding boxes. + // The ROW that this word sits in. NOT owned by the WERD_RES. + ROW* blob_row; // The denorm provides the transformation to get back to the rotated image - // coords from the chopped_word/rebuild_word BLN coords. + // coords from the chopped_word/rebuild_word BLN coords, but each blob also + // has its own denorm. DENORM denorm; // For use on chopped_word. // Unicharset used by the classifier output in best_choice and raw_choice. const UNICHARSET* uch_set; // For converting back to utf8. @@ -355,13 +198,32 @@ class WERD_RES : public ELIST_LINK { // character fragments that make up the word. // The length of chopped_word matches length of seam_array + 1 (if set). TWERD* chopped_word; // BLN chopped fragments output. - SEAMS seam_array; // Seams matching chopped_word. - WERD_CHOICE *best_choice; // tess output - WERD_CHOICE *raw_choice; // top choice permuter - // Alternative paths found during chopping/segmentation search stages - // (the first entry being a slim copy of best_choice). - GenericVector alt_choices; - GenericVector > alt_states; + // Vector of SEAM* holding chopping points matching chopped_word. + GenericVector seam_array; + // Widths of blobs in chopped_word. + GenericVector blob_widths; + // Gaps between blobs in chopped_word. blob_gaps[i] is the gap between + // blob i and blob i+1. + GenericVector blob_gaps; + // Ratings matrix contains classifier choices for each classified combination + // of blobs. The dimension is the same as the number of blobs in chopped_word + // and the leading diagonal corresponds to classifier results of the blobs + // in chopped_word. The state_ members of best_choice, raw_choice and + // best_choices all correspond to this ratings matrix and allow extraction + // of the blob choices for any given WERD_CHOICE. + MATRIX* ratings; // Owned pointer. + // Pointer to the first WERD_CHOICE in best_choices. This is the result that + // will be output from Tesseract. Note that this is now a borrowed pointer + // and should NOT be deleted. + WERD_CHOICE* best_choice; // Borrowed pointer. + // The best raw_choice found during segmentation search. Differs from the + // best_choice by being the best result according to just the character + // classifier, not taking any language model information into account. + // Unlike best_choice, the pointer IS owned by this WERD_RES. + WERD_CHOICE* raw_choice; // Owned pointer. + // Alternative results found during chopping/segmentation search stages. + // Note that being an ELIST, best_choices owns the WERD_CHOICEs. + WERD_CHOICE_LIST best_choices; // Truth bounding boxes, text and incorrect choice reason. BlamerBundle *blamer_bundle; @@ -462,6 +324,8 @@ class WERD_RES : public ELIST_LINK { InitPointers(); word = the_word; } + // Deep copies everything except the ratings MATRIX. + // To get that use deep_copy below. WERD_RES(const WERD_RES &source) { InitPointers(); *this = source; // see operator= @@ -545,7 +409,11 @@ class WERD_RES : public ELIST_LINK { void InitPointers(); void Clear(); void ClearResults(); + void ClearWordChoices(); + void ClearRatings(); + // Deep copies everything except the ratings MATRIX. + // To get that use deep_copy below. WERD_RES& operator=(const WERD_RES& source); //from this void CopySimpleFields(const WERD_RES& source); @@ -557,18 +425,28 @@ class WERD_RES : public ELIST_LINK { void InitForRetryRecognition(const WERD_RES& source); // Sets up the members used in recognition: bln_boxes, chopped_word, - // seam_array, denorm, best_choice, raw_choice. Returns false if + // seam_array, denorm. Returns false if // the word is empty and sets up fake results. If use_body_size is // true and row->body_size is set, then body_size will be used for // blob normalization instead of xheight + ascrise. This flag is for // those languages that are using CJK pitch model and thus it has to // be true if and only if tesseract->textord_use_cjk_fp_model is // true. + // If allow_detailed_fx is true, the feature extractor will receive fine + // precision outline information, allowing smoother features and better + // features on low resolution images. + // Returns false if the word is empty and sets up fake results. bool SetupForTessRecognition(const UNICHARSET& unicharset_in, tesseract::Tesseract* tesseract, Pix* pix, bool numeric_mode, bool use_body_size, + bool allow_detailed_fx, ROW *row, BLOCK* block); + // Set up the seam array, bln_boxes, best_choice, and raw_choice to empty + // accumulators from a made chopped word. We presume the fields are already + // empty. + void SetupBasicsFromChoppedWord(const UNICHARSET &unicharset_in); + // Sets up the members used in recognition: // bln_boxes, chopped_word, seam_array, denorm. // Returns false if the word is empty and sets up fake results. @@ -586,6 +464,87 @@ class WERD_RES : public ELIST_LINK { // Sets up the blamer_bundle if it is not null, using the initialized denorm. void SetupBlamerBundle(); + // Computes the blob_widths and blob_gaps from the chopped_word. + void SetupBlobWidthsAndGaps(); + + // Updates internal data to account for a new SEAM (chop) at the given + // blob_number. Fixes the ratings matrix and states in the choices, as well + // as the blob widths and gaps. + void InsertSeam(int blob_number, SEAM* seam); + + // Returns true if all the word choices except the first have adjust_factors + // worse than the given threshold. + bool AlternativeChoiceAdjustmentsWorseThan(float threshold) const; + + // Returns true if the current word is ambiguous (by number of answers or + // by dangerous ambigs.) + bool IsAmbiguous(); + + // Returns true if the ratings matrix size matches the sum of each of the + // segmentation states. + bool StatesAllValid(); + + // Prints a list of words found if debug is true or the word result matches + // the word_to_debug. + void DebugWordChoices(bool debug, const char* word_to_debug); + + // Removes from best_choices all choices which are not within a reasonable + // range of the best choice. + void FilterWordChoices(int debug_level); + + // Computes a set of distance thresholds used to control adaption. + // Compares the best choice for the current word to the best raw choice + // to determine which characters were classified incorrectly by the + // classifier. Then places a separate threshold into thresholds for each + // character in the word. If the classifier was correct, max_rating is placed + // into thresholds. If the classifier was incorrect, the mean match rating + // (error percentage) of the classifier's incorrect choice minus some margin + // is placed into thresholds. This can then be used by the caller to try to + // create a new template for the desired class that will classify the + // character with a rating better than the threshold value. The match rating + // placed into thresholds is never allowed to be below min_rating in order to + // prevent trying to make overly tight templates. + // min_rating limits how tight to make a template. + // max_rating limits how loose to make a template. + // rating_margin denotes the amount of margin to put in template. + void ComputeAdaptionThresholds(float certainty_scale, + float min_rating, + float max_rating, + float rating_margin, + float* thresholds); + + // Saves a copy of the word_choice if it has the best unadjusted rating. + // Returns true if the word_choice was the new best. + bool LogNewRawChoice(WERD_CHOICE* word_choice); + // Consumes word_choice by adding it to best_choices, (taking ownership) if + // the certainty for word_choice is some distance of the best choice in + // best_choices, or by deleting the word_choice and returning false. + // The best_choices list is kept in sorted order by rating. Duplicates are + // removed, and the list is kept no longer than max_num_choices in length. + // Returns true if the word_choice is still a valid pointer. + bool LogNewCookedChoice(int max_num_choices, bool debug, + WERD_CHOICE* word_choice); + + // Prints a brief list of all the best choices. + void PrintBestChoices() const; + + // Returns the sum of the widths of the blob between start_blob and last_blob + // inclusive. + int GetBlobsWidth(int start_blob, int last_blob); + // Returns the width of a gap between the specified blob and the next one. + int GetBlobsGap(int blob_index); + + // Returns the BLOB_CHOICE corresponding to the given index in the + // best choice word taken from the appropriate cell in the ratings MATRIX. + // Borrowed pointer, so do not delete. May return NULL if there is no + // BLOB_CHOICE matching the unichar_id at the given index. + BLOB_CHOICE* GetBlobChoice(int index) const; + + // Returns the BLOB_CHOICE_LIST corresponding to the given index in the + // best choice word taken from the appropriate cell in the ratings MATRIX. + // Borrowed pointer, so do not delete. + BLOB_CHOICE_LIST* GetBlobChoices(int index) const; + // Moves the results fields from word to this. This takes ownership of all // the data, so src can be destructed. // word1.ConsumeWordResult(word); @@ -597,10 +556,11 @@ class WERD_RES : public ELIST_LINK { void ConsumeWordResults(WERD_RES* word); // Replace the best choice and rebuild box word. - void ReplaceBestChoice(const WERD_CHOICE& choice, - const GenericVector &segmentation_state); + // choice must be from the current best_choices list. + void ReplaceBestChoice(WERD_CHOICE* choice); - // Builds the rebuild_word from the chopped_word and the best_state. + // Builds the rebuild_word and sets the best_state from the chopped_word and + // the best_choice->state. void RebuildBestState(); // Copies the chopped_word to the rebuild_word, faking a best_state as well. @@ -610,30 +570,26 @@ class WERD_RES : public ELIST_LINK { // Sets/replaces the box_word with one made from the rebuild_word. void SetupBoxWord(); - // Sets up the script positions in the output boxword using the best_choice + // Sets up the script positions in the best_choice using the best_choice // to get the unichars, and the unicharset to get the target positions. void SetScriptPositions(); - - // Returns the indices [start, end) containing the core of the word, stripped - // of any superscript digits on either side. - // (i.e., the non-footnote part of the word). - // Assumes that BoxWord is all set up for best_choice. - void WithoutFootnoteSpan(int *start, int *end) const; - - // Given an alternate word choice and segmentation state, yield the indices - // [start, end) containig the core of the word, stripped of any superscript - // digits on either side. (i.e. stripping off the footnote parts). - void WithoutFootnoteSpan( - const WERD_CHOICE &choice, const GenericVector &state, - int *start, int *end) const; + // Sets all the blobs in all the words (best choice and alternates) to be + // the given position. (When a sub/superscript is recognized as a separate + // word, it falls victim to the rule that a whole word cannot be sub or + // superscript, so this function overrides that problem.) + void SetAllScriptPositions(tesseract::ScriptPos position); // Classifies the word with some already-calculated BLOB_CHOICEs. // The choices are an array of blob_count pointers to BLOB_CHOICE, // providing a single classifier result for each blob. // The BLOB_CHOICEs are consumed and the word takes ownership. - // The number of blobs in the outword must match blob_count. + // The number of blobs in the box_word must match blob_count. void FakeClassifyWord(int blob_count, BLOB_CHOICE** choices); + // Creates a WERD_CHOICE for the word using the top choices from the leading + // diagonal of the ratings matrix. + void FakeWordFromRatings(); + // Copies the best_choice strings to the correct_text for adaption/training. void BestChoiceToCorrectText(); @@ -644,13 +600,16 @@ class WERD_RES : public ELIST_LINK { // Returns true if anything was merged. bool ConditionalBlobMerge( TessResultCallback2* class_cb, - TessResultCallback2* box_cb, - BLOB_CHOICE_LIST_CLIST *blob_choices); + TessResultCallback2* box_cb); + + // Merges 2 adjacent blobs in the result (index and index+1) and corrects + // all the data to account for the change. + void MergeAdjacentBlobs(int index); // Callback helper for fix_quotes returns a double quote if both // arguments are quote, otherwise INVALID_UNICHAR_ID. UNICHAR_ID BothQuotes(UNICHAR_ID id1, UNICHAR_ID id2); - void fix_quotes(BLOB_CHOICE_LIST_CLIST *blob_choices); + void fix_quotes(); // Callback helper for fix_hyphens returns UNICHAR_ID of - if both // arguments are hyphen, otherwise INVALID_UNICHAR_ID. @@ -658,15 +617,21 @@ class WERD_RES : public ELIST_LINK { // Callback helper for fix_hyphens returns true if box1 and box2 overlap // (assuming both on the same textline, are in order and a chopped em dash.) bool HyphenBoxesOverlap(const TBOX& box1, const TBOX& box2); - void fix_hyphens(BLOB_CHOICE_LIST_CLIST *blob_choices); + void fix_hyphens(); // Callback helper for merge_tess_fails returns a space if both // arguments are space, otherwise INVALID_UNICHAR_ID. UNICHAR_ID BothSpaces(UNICHAR_ID id1, UNICHAR_ID id2); void merge_tess_fails(); + // Returns a really deep copy of *src, including the ratings MATRIX. static WERD_RES* deep_copy(const WERD_RES* src) { - return new WERD_RES(*src); + WERD_RES* result = new WERD_RES(*src); + // That didn't copy the ratings, but we want a copy if there is one to + // begin width. + if (src->ratings != NULL) + result->ratings = src->ratings->DeepCopy(); + return result; } // Copy blobs from word_res onto this word (eliminating spaces between). diff --git a/ccstruct/params_training_featdef.cpp b/ccstruct/params_training_featdef.cpp new file mode 100644 index 0000000000..5daa7ae197 --- /dev/null +++ b/ccstruct/params_training_featdef.cpp @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////// +// File: params_training_featdef.cpp +// Description: Utility functions for params training features. +// Author: David Eger +// Created: Mon Jun 11 11:26:42 PDT 2012 +// +// (C) Copyright 2012, Google Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +/////////////////////////////////////////////////////////////////////// + +#include + +#include "params_training_featdef.h" + +namespace tesseract { + +int ParamsTrainingFeatureByName(const char *name) { + if (name == NULL) + return -1; + int array_size = sizeof(kParamsTrainingFeatureTypeName) / + sizeof(kParamsTrainingFeatureTypeName[0]); + for (int i = 0; i < array_size; i++) { + if (kParamsTrainingFeatureTypeName[i] == NULL) + continue; + if (strcmp(name, kParamsTrainingFeatureTypeName[i]) == 0) + return i; + } + return -1; +} + +} // namespace tesseract diff --git a/ccstruct/params_training_featdef.h b/ccstruct/params_training_featdef.h index ce795687e7..ff76480be0 100644 --- a/ccstruct/params_training_featdef.h +++ b/ccstruct/params_training_featdef.h @@ -25,67 +25,97 @@ namespace tesseract { +// Maximum number of unichars in the small and medium sized words +static const int kMaxSmallWordUnichars = 3; +static const int kMaxMediumWordUnichars = 6; + // Raw features extracted from a single OCR hypothesis. -// The features are non-normalized real-valued quantities with -// unbounded range and unknown distribution. +// The features are normalized (by outline length or number of unichars as +// appropriate) real-valued quantities with unbounded range and +// unknown distribution. // Normalization / binarization of these features is done at a later stage. // Note: when adding new fields to this enum make sure to modify -// kParamsTrainingRawFeatureTypeName enum accordingly. -enum ParamsTrainingRawFeatureType { - // What dictionary (if any) was this hypothesis found in. - // See PermuterType enum in ccstruct/ratngs.h for interpretation. - PTRAIN_RAW_FEATURE_DICT_MATCH_TYPE, // 0 - // Boolean indicator of whether this hypothesis is ambiguous to a known - // dictionary word (or a valid number pattern). - PTRAIN_RAW_FEATURE_UNAMBIG_DICT_MATCH, // 1 - // Shape cost of the segmentation path for this hypothesis. - PTRAIN_RAW_FEATURE_SHAPE_COST, // 2 - // Character ngram probability of the string of unichars of this hypothesis. - PTRAIN_RAW_FEATURE_NGRAM_PROB, // 3 - // Number of bad/inconsistent spots in this hypothesis. - PTRAIN_RAW_FEATURE_NUM_BAD_PUNC, // 4 - PTRAIN_RAW_FEATURE_NUM_BAD_CASE, // 5 - PTRAIN_RAW_FEATURE_NUM_BAD_CHAR_TYPE, // 6 - PTRAIN_RAW_FEATURE_NUM_BAD_SPACING, // 7 - PTRAIN_RAW_FEATURE_NUM_BAD_SCRIPT, // 8 - PTRAIN_RAW_FEATURE_NUM_BAD_FONT, // 9 - // Classifier-related features. - PTRAIN_RAW_FEATURE_WORST_CERT, // 10 - PTRAIN_RAW_FEATURE_RATING, // 11 - // Number of classifier results that came from adapted templates. - PTRAIN_RAW_FEATURE_ADAPTED, // 12 - // Features potentially useful for normalization. - PTRAIN_RAW_FEATURE_NUM_UNICHARS, // 13 - PTRAIN_RAW_FEATURE_OUTLINE_LEN, // 14 +// kParamsTrainingFeatureTypeName +enum kParamsTrainingFeatureType { + // Digits + PTRAIN_DIGITS_SHORT, // 0 + PTRAIN_DIGITS_MED, // 1 + PTRAIN_DIGITS_LONG, // 2 + // Number or pattern (NUMBER_PERM, USER_PATTERN_PERM) + PTRAIN_NUM_SHORT, // 3 + PTRAIN_NUM_MED, // 4 + PTRAIN_NUM_LONG, // 5 + // Document word (DOC_DAWG_PERM) + PTRAIN_DOC_SHORT, // 6 + PTRAIN_DOC_MED, // 7 + PTRAIN_DOC_LONG, // 8 + // Word (SYSTEM_DAWG_PERM, USER_DAWG_PERM, COMPOUND_PERM) + PTRAIN_DICT_SHORT, // 9 + PTRAIN_DICT_MED, // 10 + PTRAIN_DICT_LONG, // 11 + // Frequent word (FREQ_DAWG_PERM) + PTRAIN_FREQ_SHORT, // 12 + PTRAIN_FREQ_MED, // 13 + PTRAIN_FREQ_LONG, // 14 + PTRAIN_SHAPE_COST_PER_CHAR, // 15 + PTRAIN_NGRAM_COST_PER_CHAR, // 16 + PTRAIN_NUM_BAD_PUNC, // 17 + PTRAIN_NUM_BAD_CASE, // 18 + PTRAIN_XHEIGHT_CONSISTENCY, // 19 + PTRAIN_NUM_BAD_CHAR_TYPE, // 20 + PTRAIN_NUM_BAD_SPACING, // 21 + PTRAIN_NUM_BAD_FONT, // 22 + PTRAIN_RATING_PER_CHAR, // 23 - PTRAIN_NUM_RAW_FEATURE_TYPES + PTRAIN_NUM_FEATURE_TYPES }; -static const char * const kParamsTrainingRawFeatureTypeName[] = { - "DICT_MATCH_TYPE", // 0 - "UNAMBIG_DICT_MATCH", // 1 - "SHAPE_COST", // 2 - "NGRAM_PROB", // 3 - "NUM_BAD_PUNC", // 4 - "NUM_BAD_CASE", // 5 - "NUM_BAD_CHAR_TYPE", // 6 - "NUM_BAD_SPACING", // 7 - "NUM_BAD_SCRIPT", // 8 - "NUM_BAD_FONT", // 9 - "WORST_CERT", // 10 - "RATING", // 11 - "ADAPTED", // 12 - "NUM_UNICHARS", // 13 - "OUTLINE_LEN", // 14 +static const char * const kParamsTrainingFeatureTypeName[] = { + "PTRAIN_DIGITS_SHORT", // 0 + "PTRAIN_DIGITS_MED", // 1 + "PTRAIN_DIGITS_LONG", // 2 + "PTRAIN_NUM_SHORT", // 3 + "PTRAIN_NUM_MED", // 4 + "PTRAIN_NUM_LONG", // 5 + "PTRAIN_DOC_SHORT", // 6 + "PTRAIN_DOC_MED", // 7 + "PTRAIN_DOC_LONG", // 8 + "PTRAIN_DICT_SHORT", // 9 + "PTRAIN_DICT_MED", // 10 + "PTRAIN_DICT_LONG", // 11 + "PTRAIN_FREQ_SHORT", // 12 + "PTRAIN_FREQ_MED", // 13 + "PTRAIN_FREQ_LONG", // 14 + "PTRAIN_SHAPE_COST_PER_CHAR", // 15 + "PTRAIN_NGRAM_COST_PER_CHAR", // 16 + "PTRAIN_NUM_BAD_PUNC", // 17 + "PTRAIN_NUM_BAD_CASE", // 18 + "PTRAIN_XHEIGHT_CONSISTENCY", // 19 + "PTRAIN_NUM_BAD_CHAR_TYPE", // 20 + "PTRAIN_NUM_BAD_SPACING", // 21 + "PTRAIN_NUM_BAD_FONT", // 22 + "PTRAIN_RATING_PER_CHAR", // 23 }; +// Returns the index of the given feature (by name), +// or -1 meaning the feature is unknown. +int ParamsTrainingFeatureByName(const char *name); + + // Entry with features extracted from a single OCR hypothesis for a word. struct ParamsTrainingHypothesis { - ParamsTrainingHypothesis() { - for (int i = 0; i < PTRAIN_NUM_RAW_FEATURE_TYPES; ++i) features[i] = 0.0; + ParamsTrainingHypothesis() : cost(0.0) { + memset(features, 0, sizeof(float) * PTRAIN_NUM_FEATURE_TYPES); + } + ParamsTrainingHypothesis(const ParamsTrainingHypothesis &other) { + memcpy(features, other.features, + sizeof(float) * PTRAIN_NUM_FEATURE_TYPES); + str = other.str; + cost = other.cost; } - float features[PTRAIN_NUM_RAW_FEATURE_TYPES]; + float features[PTRAIN_NUM_FEATURE_TYPES]; STRING str; // string corresponding to word hypothesis (for debugging) + float cost; // path cost computed by segsearch }; // A list of hypotheses explored during one run of segmentation search. @@ -104,9 +134,10 @@ class ParamsTrainingBundle { } // Adds a new ParamsTrainingHypothesis to the current hypothesis list // and returns the reference to the newly added entry. - ParamsTrainingHypothesis &AddHypothesis() { + ParamsTrainingHypothesis &AddHypothesis( + const ParamsTrainingHypothesis &other) { if (hyp_list_vec.empty()) StartHypothesisList(); - hyp_list_vec.back().push_back(ParamsTrainingHypothesis()); + hyp_list_vec.back().push_back(ParamsTrainingHypothesis(other)); return hyp_list_vec.back().back(); } diff --git a/ccstruct/ratngs.cpp b/ccstruct/ratngs.cpp index c376fc909b..30608bb46f 100644 --- a/ccstruct/ratngs.cpp +++ b/ccstruct/ratngs.cpp @@ -19,13 +19,33 @@ #include "ratngs.h" +#include "blobs.h" #include "callcpp.h" #include "genericvector.h" +#include "matrix.h" +#include "normalis.h" // kBlnBaselineOffset. #include "unicharset.h" -ELISTIZE (BLOB_CHOICE) CLISTIZE (BLOB_CHOICE_LIST) CLISTIZE (WERD_CHOICE); +using tesseract::ScriptPos; + +ELISTIZE(BLOB_CHOICE); +ELISTIZE(WERD_CHOICE); const float WERD_CHOICE::kBadRating = 100000.0; +// Min offset in baseline-normalized coords to make a character a subscript. +const int kMinSubscriptOffset = 20; +// Min offset in baseline-normalized coords to make a character a superscript. +const int kMinSuperscriptOffset = 20; +// Max y of bottom of a drop-cap blob. +const int kMaxDropCapBottom = -128; +// Max fraction of x-height to use as denominator in measuring x-height overlap. +const double kMaxOverlapDenominator = 0.125; +// Min fraction of x-height range that should be in agreement for matching +// x-heights. +const double kMinXHeightMatch = 0.5; +// Max tolerance on baseline position as a fraction of x-height for matching +// baselines. +const double kMaxBaselineDrift = 0.0625; static const char kPermuterTypeNoPerm[] = "None"; static const char kPermuterTypePuncPerm[] = "Punctuation"; @@ -68,20 +88,20 @@ BLOB_CHOICE::BLOB_CHOICE(UNICHAR_ID src_unichar_id, // character id inT16 src_fontinfo_id, // font inT16 src_fontinfo_id2, // 2nd choice font int src_script_id, // script - inT16 min_xheight, // min xheight allowed - inT16 max_xheight, // max xheight by this char - bool adapted // adapted match or not - ) { + float min_xheight, // min xheight allowed + float max_xheight, // max xheight by this char + float yshift, // yshift out of position + BlobChoiceClassifier c) { // adapted match or other unichar_id_ = src_unichar_id; rating_ = src_rating; certainty_ = src_cert; fontinfo_id_ = src_fontinfo_id; fontinfo_id2_ = src_fontinfo_id2; script_id_ = src_script_id; - language_model_state_ = NULL; min_xheight_ = min_xheight; max_xheight_ = max_xheight; - adapted_ = adapted; + yshift_ = yshift; + classifier_ = c; } /** @@ -96,12 +116,75 @@ BLOB_CHOICE::BLOB_CHOICE(const BLOB_CHOICE &other) { fontinfo_id_ = other.fontinfo_id(); fontinfo_id2_ = other.fontinfo_id2(); script_id_ = other.script_id(); - language_model_state_ = NULL; + matrix_cell_ = other.matrix_cell_; min_xheight_ = other.min_xheight_; max_xheight_ = other.max_xheight_; - adapted_ = other.adapted_; + yshift_ = other.yshift(); + classifier_ = other.classifier_; } +// Returns true if *this and other agree on the baseline and x-height +// to within some tolerance based on a given estimate of the x-height. +bool BLOB_CHOICE::PosAndSizeAgree(const BLOB_CHOICE& other, float x_height, + bool debug) const { + double baseline_diff = fabs(yshift() - other.yshift()); + if (baseline_diff > kMaxBaselineDrift * x_height) { + if (debug) { + tprintf("Baseline diff %g for %d v %d\n", + baseline_diff, unichar_id_, other.unichar_id_); + } + return false; + } + double this_range = max_xheight() - min_xheight(); + double other_range = other.max_xheight() - other.min_xheight(); + double denominator = ClipToRange(MIN(this_range, other_range), + 1.0, kMaxOverlapDenominator * x_height); + double overlap = MIN(max_xheight(), other.max_xheight()) - + MAX(min_xheight(), other.min_xheight()); + overlap /= denominator; + if (debug) { + tprintf("PosAndSize for %d v %d: bl diff = %g, ranges %g, %g / %g ->%g\n", + unichar_id_, other.unichar_id_, baseline_diff, + this_range, other_range, denominator, overlap); + } + + return overlap >= kMinXHeightMatch; +} + +// Helper to find the BLOB_CHOICE in the bc_list that matches the given +// unichar_id, or NULL if there is no match. +BLOB_CHOICE* FindMatchingChoice(UNICHAR_ID char_id, + BLOB_CHOICE_LIST* bc_list) { + // Find the corresponding best BLOB_CHOICE. + BLOB_CHOICE_IT choice_it(bc_list); + for (choice_it.mark_cycle_pt(); !choice_it.cycled_list(); + choice_it.forward()) { + BLOB_CHOICE* choice = choice_it.data(); + if (choice->unichar_id() == char_id) { + return choice; + } + } + return NULL; +} + +const char *WERD_CHOICE::permuter_name(uinT8 permuter) { + return kPermuterTypeNames[permuter]; +} + +namespace tesseract { + +const char *ScriptPosToString(enum ScriptPos script_pos) { + switch (script_pos) { + case SP_NORMAL: return "NORM"; + case SP_SUBSCRIPT: return "SUB"; + case SP_SUPERSCRIPT: return "SUPER"; + case SP_DROPCAP: return "DROPC"; + } + return "SP_UNKNOWN"; +} + +} // namespace tesseract. + /** * WERD_CHOICE::WERD_CHOICE * @@ -111,16 +194,13 @@ BLOB_CHOICE::BLOB_CHOICE(const BLOB_CHOICE &other) { WERD_CHOICE::WERD_CHOICE(const char *src_string, const UNICHARSET &unicharset) : unicharset_(&unicharset){ - STRING src_lengths; - const char *ptr = src_string; - const char *end = src_string + strlen(src_string); - int step = unicharset.step(ptr); - for (; ptr < end && step > 0; - step = unicharset.step(ptr), src_lengths += step, ptr += step); - if (step != 0 && ptr == end) { - this->init(src_string, src_lengths.string(), - 0.0, 0.0, NO_PERM); - } else { // there must have been an invalid unichar in the string + GenericVector encoding; + GenericVector lengths; + if (unicharset.encode_string(src_string, true, &encoding, &lengths, NULL)) { + lengths.push_back('\0'); + STRING src_lengths = &lengths[0]; + this->init(src_string, src_lengths.string(), 0.0, 0.0, NO_PERM); + } else { // There must have been an invalid unichar in the string. this->init(8); this->make_bad(); } @@ -152,13 +232,16 @@ void WERD_CHOICE::init(const char *src_string, int unichar_length = src_lengths ? src_lengths[i] : 1; unichar_ids_[i] = unicharset_->unichar_to_id(src_string+offset, unichar_length); - fragment_lengths_[i] = 1; + state_[i] = 1; + certainties_[i] = src_certainty; offset += unichar_length; } } + adjust_factor_ = 1.0f; rating_ = src_rating; certainty_ = src_certainty; permuter_ = src_permuter; + dangerous_ambig_found_ = false; } /** @@ -166,25 +249,46 @@ void WERD_CHOICE::init(const char *src_string, */ WERD_CHOICE::~WERD_CHOICE() { delete[] unichar_ids_; - delete[] fragment_lengths_; - delete_blob_choices(); + delete[] script_pos_; + delete[] state_; + delete[] certainties_; } const char *WERD_CHOICE::permuter_name() const { return kPermuterTypeNames[permuter_]; } -/** - * WERD_CHOICE::set_blob_choices - * - * Delete current blob_choices. Set the blob_choices to the given new - * list. - */ -void WERD_CHOICE::set_blob_choices(BLOB_CHOICE_LIST_CLIST *blob_choices) { - if (blob_choices_ != blob_choices) { - delete_blob_choices(); - blob_choices_ = blob_choices; - } +// Returns the BLOB_CHOICE_LIST corresponding to the given index in the word, +// taken from the appropriate cell in the ratings MATRIX. +// Borrowed pointer, so do not delete. +BLOB_CHOICE_LIST* WERD_CHOICE::blob_choices(int index, MATRIX* ratings) const { + MATRIX_COORD coord = MatrixCoord(index); + BLOB_CHOICE_LIST* result = ratings->get(coord.col, coord.row); + if (result == NULL) { + result = new BLOB_CHOICE_LIST; + ratings->put(coord.col, coord.row, result); + } + return result; +} + +// Returns the MATRIX_COORD corresponding to the location in the ratings +// MATRIX for the given index into the word. +MATRIX_COORD WERD_CHOICE::MatrixCoord(int index) const { + int col = 0; + for (int i = 0; i < index; ++i) + col += state_[i]; + int row = col + state_[index] - 1; + return MATRIX_COORD(col, row); +} + +// Sets the entries for the given index from the BLOB_CHOICE, assuming +// unit fragment lengths, but setting the state for this index to blob_count. +void WERD_CHOICE::set_blob_choice(int index, int blob_count, + const BLOB_CHOICE* blob_choice) { + unichar_ids_[index] = blob_choice->unichar_id(); + script_pos_[index] = tesseract::SP_NORMAL; + state_[index] = blob_count; + certainties_[index] = blob_choice->certainty(); } @@ -211,9 +315,18 @@ bool WERD_CHOICE::contains_unichar_id(UNICHAR_ID unichar_id) const { */ void WERD_CHOICE::remove_unichar_ids(int start, int num) { ASSERT_HOST(start >= 0 && start + num <= length_); - for (int i = start; i+num < length_; ++i) { - unichar_ids_[i] = unichar_ids_[i+num]; - fragment_lengths_[i] = fragment_lengths_[i+num]; + // Accumulate the states to account for the merged blobs. + for (int i = 0; i < num; ++i) { + if (start > 0) + state_[start - 1] += state_[start + i]; + else if (start + num < length_) + state_[start + num] += state_[start + i]; + } + for (int i = start; i + num < length_; ++i) { + unichar_ids_[i] = unichar_ids_[i + num]; + script_pos_[i] = script_pos_[i + num]; + state_[i] = state_[i + num]; + certainties_[i] = certainties_[i + num]; } length_ -= num; } @@ -224,7 +337,7 @@ void WERD_CHOICE::remove_unichar_ids(int start, int num) { * Reverses and mirrors unichars in unichar_ids. */ void WERD_CHOICE::reverse_and_mirror_unichar_ids() { - for (int i = 0; i < length_/2; ++i) { + for (int i = 0; i < length_ / 2; ++i) { UNICHAR_ID tmp_id = unichar_ids_[i]; unichar_ids_[i] = unicharset_->get_mirror(unichar_ids_[length_-1-i]); unichar_ids_[length_-1-i] = unicharset_->get_mirror(tmp_id); @@ -255,6 +368,23 @@ void WERD_CHOICE::punct_stripped(int *start, int *end) const { (*end)++; } +void WERD_CHOICE::GetNonSuperscriptSpan(int *pstart, int *pend) const { + int end = length(); + while (end > 0 && + unicharset_->get_isdigit(unichar_ids_[end - 1]) && + BlobPosition(end - 1) == tesseract::SP_SUPERSCRIPT) { + end--; + } + int start = 0; + while (start < end && + unicharset_->get_isdigit(unichar_ids_[start]) && + BlobPosition(start) == tesseract::SP_SUPERSCRIPT) { + start++; + } + *pstart = start; + *pend = end; +} + WERD_CHOICE WERD_CHOICE::shallow_copy(int start, int end) const { ASSERT_HOST(start >= 0 && start <= length_); ASSERT_HOST(end >= 0 && end <= length_); @@ -262,7 +392,7 @@ WERD_CHOICE WERD_CHOICE::shallow_copy(int start, int end) const { WERD_CHOICE retval(unicharset_, end - start); for (int i = start; i < end; i++) { retval.append_unichar_id_space_allocated( - unichar_ids_[i], fragment_lengths_[i], 0.0f, 0.0f); + unichar_ids_[i], state_[i], 0.0f, certainties_[i]); } return retval; } @@ -310,12 +440,12 @@ void WERD_CHOICE::string_and_lengths(STRING *word_str, * and call append_unichar_id_space_allocated(). */ void WERD_CHOICE::append_unichar_id( - UNICHAR_ID unichar_id, char fragment_length, + UNICHAR_ID unichar_id, int blob_count, float rating, float certainty) { if (length_ == reserved_) { this->double_the_size(); } - this->append_unichar_id_space_allocated(unichar_id, fragment_length, + this->append_unichar_id_space_allocated(unichar_id, blob_count, rating, certainty); } @@ -327,59 +457,31 @@ void WERD_CHOICE::append_unichar_id( * If the permuters are NOT the same the permuter is set to COMPOUND_PERM */ WERD_CHOICE & WERD_CHOICE::operator+= (const WERD_CHOICE & second) { - // TODO(daria): find out why the choice was cleared this way if any - // of the pieces are empty. Add the description of this behavior - // to the comments. - // if (word_string.length () == 0 || second.word_string.length () == 0) { - // word_string = NULL; //make it empty - // word_lengths = NULL; - // delete_blob_choices(); - // } else { ASSERT_HOST(unicharset_ == second.unicharset_); while (reserved_ < length_ + second.length()) { this->double_the_size(); } const UNICHAR_ID *other_unichar_ids = second.unichar_ids(); - const char *other_fragment_lengths = second.fragment_lengths(); for (int i = 0; i < second.length(); ++i) { unichar_ids_[length_ + i] = other_unichar_ids[i]; - fragment_lengths_[length_ + i] = other_fragment_lengths[i]; + state_[length_ + i] = second.state_[i]; + certainties_[length_ + i] = second.certainties_[i]; + script_pos_[length_ + i] = second.BlobPosition(i); } length_ += second.length(); + if (second.adjust_factor_ > adjust_factor_) + adjust_factor_ = second.adjust_factor_; rating_ += second.rating(); // add ratings if (second.certainty() < certainty_) // take min certainty_ = second.certainty(); + if (second.dangerous_ambig_found_) + dangerous_ambig_found_ = true; if (permuter_ == NO_PERM) { permuter_ = second.permuter(); } else if (second.permuter() != NO_PERM && second.permuter() != permuter_) { permuter_ = COMPOUND_PERM; } - - // Append a deep copy of second blob_choices if it exists. - if (second.blob_choices_ != NULL) { - if (this->blob_choices_ == NULL) - this->blob_choices_ = new BLOB_CHOICE_LIST_CLIST; - - BLOB_CHOICE_LIST_C_IT this_blob_choices_it; - BLOB_CHOICE_LIST_C_IT second_blob_choices_it; - - this_blob_choices_it.set_to_list(this->blob_choices_); - this_blob_choices_it.move_to_last(); - - second_blob_choices_it.set_to_list(second.blob_choices_); - - for (second_blob_choices_it.mark_cycle_pt(); - !second_blob_choices_it.cycled_list(); - second_blob_choices_it.forward()) { - - BLOB_CHOICE_LIST* blob_choices_copy = new BLOB_CHOICE_LIST(); - blob_choices_copy->deep_copy(second_blob_choices_it.data(), - &BLOB_CHOICE::deep_copy); - - this_blob_choices_it.add_after_then_move(blob_choices_copy); - } - } return *this; } @@ -397,55 +499,202 @@ WERD_CHOICE& WERD_CHOICE::operator=(const WERD_CHOICE& source) { unicharset_ = source.unicharset_; const UNICHAR_ID *other_unichar_ids = source.unichar_ids(); - const char *other_fragment_lengths = source.fragment_lengths(); for (int i = 0; i < source.length(); ++i) { unichar_ids_[i] = other_unichar_ids[i]; - fragment_lengths_[i] = other_fragment_lengths[i]; + state_[i] = source.state_[i]; + certainties_[i] = source.certainties_[i]; + script_pos_[i] = source.BlobPosition(i); } length_ = source.length(); + adjust_factor_ = source.adjust_factor_; rating_ = source.rating(); certainty_ = source.certainty(); + min_x_height_ = source.min_x_height(); + max_x_height_ = source.max_x_height(); permuter_ = source.permuter(); - fragment_mark_ = source.fragment_mark(); - - // Delete existing blob_choices - this->delete_blob_choices(); + dangerous_ambig_found_ = source.dangerous_ambig_found_; + return *this; +} - // Deep copy blob_choices of source - if (source.blob_choices_ != NULL) { - BLOB_CHOICE_LIST_C_IT this_blob_choices_it; - BLOB_CHOICE_LIST_C_IT source_blob_choices_it; +// Sets up the script_pos_ member using the blobs_list to get the bln +// bounding boxes, *this to get the unichars, and this->unicharset +// to get the target positions. If small_caps is true, sub/super are not +// considered, but dropcaps are. +// NOTE: blobs_list should be the chopped_word blobs. (Fully segemented.) +void WERD_CHOICE::SetScriptPositions(bool small_caps, TWERD* word) { + // Since WERD_CHOICE isn't supposed to depend on a Tesseract, + // we don't have easy access to the flags Tesseract stores. Therefore, debug + // for this module is hard compiled in. + int debug = 0; + + // Initialize to normal. + for (int i = 0; i < length_; ++i) + script_pos_[i] = tesseract::SP_NORMAL; + if (word->blobs.empty()) + return; - this->blob_choices_ = new BLOB_CHOICE_LIST_CLIST(); + int position_counts[4]; + for (int i = 0; i < 4; i++) { + position_counts[i] = 0; + } + + int chunk_index = 0; + for (int blob_index = 0; blob_index < length_; ++blob_index, ++chunk_index) { + TBLOB* tblob = word->blobs[chunk_index]; + int uni_id = unichar_id(blob_index); + TBOX blob_box = tblob->bounding_box(); + if (state_ != NULL) { + for (int i = 1; i < state_[blob_index]; ++i) { + ++chunk_index; + tblob = word->blobs[chunk_index]; + blob_box += tblob->bounding_box(); + } + } + script_pos_[blob_index] = ScriptPositionOf(false, *unicharset_, blob_box, + uni_id); + if (small_caps && script_pos_[blob_index] != tesseract::SP_DROPCAP) { + script_pos_[blob_index] = tesseract::SP_NORMAL; + } + position_counts[script_pos_[blob_index]]++; + } + // If almost everything looks like a superscript or subscript, + // we most likely just got the baseline wrong. + if (position_counts[tesseract::SP_SUBSCRIPT] > 0.75 * length_ || + position_counts[tesseract::SP_SUPERSCRIPT] > 0.75 * length_) { + if (debug >= 2) { + tprintf("Most characters of %s are subscript or superscript.\n" + "That seems wrong, so I'll assume we got the baseline wrong\n", + unichar_string().string()); + } + for (int i = 0; i < length_; i++) { + ScriptPos sp = script_pos_[i]; + if (sp == tesseract::SP_SUBSCRIPT || sp == tesseract::SP_SUPERSCRIPT) { + position_counts[sp]--; + position_counts[tesseract::SP_NORMAL]++; + script_pos_[i] = tesseract::SP_NORMAL; + } + } + } - this_blob_choices_it.set_to_list(this->blob_choices_); - source_blob_choices_it.set_to_list(source.blob_choices_); + if ((debug >= 1 && position_counts[tesseract::SP_NORMAL] < length_) || + debug >= 2) { + tprintf("SetScriptPosition on %s\n", unichar_string().string()); + int chunk_index = 0; + for (int blob_index = 0; blob_index < length_; ++blob_index) { + if (debug >= 2 || script_pos_[blob_index] != tesseract::SP_NORMAL) { + TBLOB* tblob = word->blobs[chunk_index]; + ScriptPositionOf(true, *unicharset_, tblob->bounding_box(), + unichar_id(blob_index)); + } + chunk_index += state_ != NULL ? state_[blob_index] : 1; + } + } +} +// Sets the script_pos_ member from some source positions with a given length. +void WERD_CHOICE::SetScriptPositions(const tesseract::ScriptPos* positions, + int length) { + ASSERT_HOST(length == length_); + if (positions != script_pos_) { + delete [] script_pos_; + script_pos_ = new ScriptPos[length]; + memcpy(script_pos_, positions, sizeof(positions[0]) * length); + } +} +// Sets all the script_pos_ positions to the given position. +void WERD_CHOICE::SetAllScriptPositions(tesseract::ScriptPos position) { + for (int i = 0; i < length_; ++i) + script_pos_[i] = position; +} - for (source_blob_choices_it.mark_cycle_pt(); - !source_blob_choices_it.cycled_list(); - source_blob_choices_it.forward()) { +/* static */ +ScriptPos WERD_CHOICE::ScriptPositionOf(bool print_debug, + const UNICHARSET& unicharset, + const TBOX& blob_box, + UNICHAR_ID unichar_id) { + ScriptPos retval = tesseract::SP_NORMAL; + int top = blob_box.top(); + int bottom = blob_box.bottom(); + int min_bottom, max_bottom, min_top, max_top; + unicharset.get_top_bottom(unichar_id, + &min_bottom, &max_bottom, + &min_top, &max_top); + + int sub_thresh_top = min_top - kMinSubscriptOffset; + int sub_thresh_bot = kBlnBaselineOffset - kMinSubscriptOffset; + int sup_thresh_bot = max_bottom + kMinSuperscriptOffset; + if (bottom <= kMaxDropCapBottom) { + retval = tesseract::SP_DROPCAP; + } else if (top < sub_thresh_top && bottom < sub_thresh_bot) { + retval = tesseract::SP_SUBSCRIPT; + } else if (bottom > sup_thresh_bot) { + retval = tesseract::SP_SUPERSCRIPT; + } + + if (print_debug) { + const char *pos = ScriptPosToString(retval); + tprintf("%s Character %s[bot:%d top: %d] " + "bot_range[%d,%d] top_range[%d, %d] " + "sub_thresh[bot:%d top:%d] sup_thresh_bot %d\n", + pos, unicharset.id_to_unichar(unichar_id), + bottom, top, + min_bottom, max_bottom, min_top, max_top, + sub_thresh_bot, sub_thresh_top, + sup_thresh_bot); + } + return retval; +} - BLOB_CHOICE_LIST* blob_choices_copy = new BLOB_CHOICE_LIST(); - blob_choices_copy->deep_copy(source_blob_choices_it.data(), - &BLOB_CHOICE::deep_copy); +// Returns the script-id (eg Han) of the dominant script in the word. +int WERD_CHOICE::GetTopScriptID() const { + int max_script = unicharset_->get_script_table_size(); + int *sid = new int[max_script]; + int x; + for (x = 0; x < max_script; x++) sid[x] = 0; + for (x = 0; x < length_; ++x) { + int script_id = unicharset_->get_script(unichar_id(x)); + sid[script_id]++; + } + if (unicharset_->han_sid() != unicharset_->null_sid()) { + // Add the Hiragana & Katakana counts to Han and zero them out. + if (unicharset_->hiragana_sid() != unicharset_->null_sid()) { + sid[unicharset_->han_sid()] += sid[unicharset_->hiragana_sid()]; + sid[unicharset_->hiragana_sid()] = 0; + } + if (unicharset_->katakana_sid() != unicharset_->null_sid()) { + sid[unicharset_->han_sid()] += sid[unicharset_->katakana_sid()]; + sid[unicharset_->katakana_sid()] = 0; + } + } + // Note that high script ID overrides lower one on a tie, thus biasing + // towards non-Common script (if sorted that way in unicharset file). + int max_sid = 0; + for (x = 1; x < max_script; x++) + if (sid[x] >= sid[max_sid]) max_sid = x; + if (sid[max_sid] < length_ / 2) + max_sid = unicharset_->null_sid(); + delete[] sid; + return max_sid; +} - this_blob_choices_it.add_after_then_move(blob_choices_copy); +// Fixes the state_ for a chop at the given blob_posiiton. +void WERD_CHOICE::UpdateStateForSplit(int blob_position) { + int total_chunks = 0; + for (int i = 0; i < length_; ++i) { + total_chunks += state_[i]; + if (total_chunks > blob_position) { + ++state_[i]; + return; } } - return *this; } -/********************************************************************** - * WERD_CHOICE::delete_blob_choices - * - * Clear the blob_choices list, delete it and set it to NULL. - **********************************************************************/ -void WERD_CHOICE::delete_blob_choices() { - if (blob_choices_ != NULL) { - blob_choices_->deep_clear(); - delete blob_choices_; - blob_choices_ = NULL; +// Returns the sum of all the state elements, being the total number of blobs. +int WERD_CHOICE::TotalOfStates() const { + int total_chunks = 0; + for (int i = 0; i < length_; ++i) { + total_chunks += state_[i]; } + return total_chunks; } /** @@ -453,32 +702,87 @@ void WERD_CHOICE::delete_blob_choices() { * * Print WERD_CHOICE to stdout. */ -const void WERD_CHOICE::print(const char *msg) const { - tprintf("%s WERD_CHOICE:\n", msg); - tprintf("length_ %d reserved_ %d permuter_ %d\n", - length_, reserved_, permuter_); - tprintf("rating_ %.4f certainty_ %.4f", rating_, certainty_); - if (fragment_mark_) { - tprintf(" fragment_mark_ true"); +void WERD_CHOICE::print(const char *msg) const { + tprintf("%s : ", msg); + for (int i = 0; i < length_; ++i) { + tprintf("%s", unicharset_->id_to_unichar(unichar_ids_[i])); } - tprintf("\n"); - if (unichar_string_.length() > 0) { - tprintf("unichar_string_ %s unichar_lengths_ %s\n", - unichar_string_.string(), unichar_lengths_.string()); + tprintf(" : R=%g, C=%g, F=%g, Perm=%d, xht=[%g,%g], ambig=%d\n", + rating_, certainty_, adjust_factor_, permuter_, + min_x_height_, max_x_height_, dangerous_ambig_found_); + tprintf("pos"); + for (int i = 0; i < length_; ++i) { + tprintf("\t%s", ScriptPosToString(script_pos_[i])); } - tprintf("unichar_ids: "); - int i; - for (i = 0; i < length_; ++i) { - tprintf("%d ", unichar_ids_[i]); + tprintf("\nstr"); + for (int i = 0; i < length_; ++i) { + tprintf("\t%s", unicharset_->id_to_unichar(unichar_ids_[i])); } - tprintf("\nfragment_lengths_: "); - for (i = 0; i < length_; ++i) { - tprintf("%d ", fragment_lengths_[i]); + tprintf("\nstate:"); + for (int i = 0; i < length_; ++i) { + tprintf("\t%d ", state_[i]); + } + tprintf("\nC"); + for (int i = 0; i < length_; ++i) { + tprintf("\t%.3f", certainties_[i]); } tprintf("\n"); - fflush(stdout); } +// Prints the segmentation state with an introductory message. +void WERD_CHOICE::print_state(const char *msg) const { + tprintf("%s", msg); + for (int i = 0; i < length_; ++i) + tprintf(" %d", state_[i]); + tprintf("\n"); +} + +// Displays the segmentation state of *this (if not the same as the last +// one displayed) and waits for a click in the window. +void WERD_CHOICE::DisplaySegmentation(TWERD* word) { +#ifndef GRAPHICS_DISABLED + // Number of different colors to draw with. + const int kNumColors = 6; + static ScrollView *segm_window = NULL; + // Check the state against the static prev_drawn_state. + static GenericVector prev_drawn_state; + bool already_done = prev_drawn_state.size() == length_; + if (!already_done) prev_drawn_state.init_to_size(length_, 0); + for (int i = 0; i < length_; ++i) { + if (prev_drawn_state[i] != state_[i]) { + already_done = false; + } + prev_drawn_state[i] = state_[i]; + } + if (already_done || word->blobs.empty()) return; + + // Create the window if needed. + if (segm_window == NULL) { + segm_window = new ScrollView("Segmentation", 5, 10, 500, 256, + 2000.0, 256.0, true); + } else { + segm_window->Clear(); + } + + TBOX bbox; + int blob_index = 0; + for (int c = 0; c < length_; ++c) { + ScrollView::Color color = + static_cast(c % kNumColors + 3); + for (int i = 0; i < state_[c]; ++i, ++blob_index) { + TBLOB* blob = word->blobs[blob_index]; + bbox += blob->bounding_box(); + blob->plot(segm_window, color, color); + } + } + segm_window->ZoomToRectangle(bbox.left(), bbox.top(), + bbox.right(), bbox.bottom()); + segm_window->Update(); + window_wait(segm_window); +#endif +} + + bool EqualIgnoringCaseAndTerminalPunct(const WERD_CHOICE &word1, const WERD_CHOICE &word2) { const UNICHARSET *uchset = word1.unicharset(); @@ -526,114 +830,3 @@ void print_ratings_list(const char *msg, tprintf("\n"); fflush(stdout); } - -/** - * print_ratings_list - * - * Print ratings list (unichar ids only). - */ -void print_ratings_list(const char *msg, BLOB_CHOICE_LIST *ratings) { - if (ratings->length() == 0) { - tprintf("%s:\n", msg); - return; - } - if (*msg != '\0') { - tprintf("%s\n", msg); - } - BLOB_CHOICE_IT c_it; - c_it.set_to_list(ratings); - for (c_it.mark_cycle_pt(); !c_it.cycled_list(); c_it.forward()) { - c_it.data()->print(NULL); - if (!c_it.at_last()) tprintf("\n"); - } - tprintf("\n"); - fflush(stdout); -} - -/** - * print_ratings_info - * - * Send all the ratings out to the logfile. - * - * @param fp file to use - * @param ratings list of results - * @param current_unicharset unicharset that can be used - * for id-to-unichar conversion - */ -void print_ratings_info(FILE *fp, - BLOB_CHOICE_LIST *ratings, - const UNICHARSET ¤t_unicharset) { - inT32 index; // to list - const char* first_char = NULL; // character - FLOAT32 first_rat; // rating - FLOAT32 first_cert; // certainty - const char* sec_char = NULL; // character - FLOAT32 sec_rat = 0.0f; // rating - FLOAT32 sec_cert = 0.0f; // certainty - BLOB_CHOICE_IT c_it = ratings; // iterator - - index = ratings->length(); - if (index > 0) { - first_char = current_unicharset.id_to_unichar(c_it.data()->unichar_id()); - first_rat = c_it.data()->rating(); - first_cert = -c_it.data()->certainty(); - if (index > 1) { - sec_char = current_unicharset.id_to_unichar( - c_it.data_relative(1)->unichar_id()); - sec_rat = c_it.data_relative(1)->rating(); - sec_cert = -c_it.data_relative(1)->certainty(); - } else { - sec_char = NULL; - sec_rat = -1; - sec_cert = -1; - } - } else { - first_char = NULL; - first_rat = -1; - first_cert = -1; - } - if (first_char != NULL && (*first_char == '\0' || *first_char == ' ')) - first_char = NULL; - if (sec_char != NULL && (*sec_char == '\0' || *sec_char == ' ')) - sec_char = NULL; - tprintf(" " INT32FORMAT " %s %g %g %s %g %g\n", - ratings->length(), - first_char != NULL ? first_char : "~", - first_rat, first_cert, sec_char != NULL ? sec_char : "~", - sec_rat, sec_cert); -} - -/** - * print_char_choices_list - */ -void print_char_choices_list(const char *msg, - const BLOB_CHOICE_LIST_VECTOR &char_choices, - const UNICHARSET ¤t_unicharset, - BOOL8 detailed) { - if (*msg != '\0') tprintf("%s\n", msg); - for (int x = 0; x < char_choices.length(); ++x) { - BLOB_CHOICE_IT c_it; - c_it.set_to_list(char_choices.get(x)); - tprintf("\nchar[%d]: %s\n", x, - current_unicharset.debug_str( c_it.data()->unichar_id()).string()); - if (detailed) - print_ratings_list("", char_choices.get(x), current_unicharset); - } -} - -/** - * print_word_alternates_list - */ -void print_word_alternates_list( - WERD_CHOICE *word, - GenericVector *alternates) { - if (!word || !alternates) return; - - STRING alternates_str; - for (int i = 0; i < alternates->size(); i++) { - if (i > 0) alternates_str += "\", \""; - alternates_str += alternates->get(i)->unichar_string(); - } - tprintf("Alternates for \"%s\": {\"%s\"}\n", - word->unichar_string().string(), alternates_str.string()); -} diff --git a/ccstruct/ratngs.h b/ccstruct/ratngs.h index e09778de09..6a31cf3ee9 100644 --- a/ccstruct/ratngs.h +++ b/ccstruct/ratngs.h @@ -23,11 +23,27 @@ #include #include "clst.h" +#include "elst.h" #include "genericvector.h" +#include "matrix.h" #include "unichar.h" #include "unicharset.h" #include "werd.h" +class MATRIX; +class TBLOB; +class TWERD; + +// Enum to describe the source of a BLOB_CHOICE to make it possible to determine +// whether a blob has been classified by inspecting the BLOB_CHOICEs. +enum BlobChoiceClassifier { + BCC_STATIC_CLASSIFIER, // From the char_norm classifier. + BCC_ADAPTED_CLASSIFIER, // From the adaptive classifier. + BCC_SPECKLE_CLASSIFIER, // Backup for failed classification. + BCC_AMBIG, // Generated by ambiguity detection. + BCC_FAKE, // From some other process. +}; + class BLOB_CHOICE: public ELIST_LINK { public: @@ -38,20 +54,23 @@ class BLOB_CHOICE: public ELIST_LINK rating_ = MAX_FLOAT32; certainty_ = -MAX_FLOAT32; script_id_ = -1; - language_model_state_ = NULL; - min_xheight_ = 0; - max_xheight_ = 0; - adapted_ = false; + xgap_before_ = 0; + xgap_after_ = 0; + min_xheight_ = 0.0f; + max_xheight_ = 0.0f; + yshift_ = 0.0f; + classifier_ = BCC_FAKE; } BLOB_CHOICE(UNICHAR_ID src_unichar_id, // character id float src_rating, // rating float src_cert, // certainty - inT16 src_fontinfo_id, // font - inT16 src_fontinfo_id2, // 2nd choice font + inT16 src_fontinfo_id, // font + inT16 src_fontinfo_id2, // 2nd choice font int script_id, // script - inT16 min_xheight, // min xheight in image pixel units - inT16 max_xheight, // max xheight allowed by this char - bool adapted); // adapted match or not + float min_xheight, // min xheight in image pixel units + float max_xheight, // max xheight allowed by this char + float yshift, // the larger of y shift (top or bottom) + BlobChoiceClassifier c); // adapted match or other BLOB_CHOICE(const BLOB_CHOICE &other); ~BLOB_CHOICE() {} @@ -73,8 +92,8 @@ class BLOB_CHOICE: public ELIST_LINK int script_id() const { return script_id_; } - void *language_model_state() { - return language_model_state_; + const MATRIX_COORD& matrix_cell() { + return matrix_cell_; } inT16 xgap_before() const { return xgap_before_; @@ -82,14 +101,25 @@ class BLOB_CHOICE: public ELIST_LINK inT16 xgap_after() const { return xgap_after_; } - inT16 min_xheight() const { + float min_xheight() const { return min_xheight_; } - inT16 max_xheight() const { + float max_xheight() const { return max_xheight_; } - bool adapted() const { - return adapted_; + float yshift() const { + return yshift_; + } + BlobChoiceClassifier classifier() const { + return classifier_; + } + bool IsAdapted() const { + return classifier_ == BCC_ADAPTED_CLASSIFIER; + } + bool IsClassified() const { + return classifier_ == BCC_STATIC_CLASSIFIER || + classifier_ == BCC_ADAPTED_CLASSIFIER || + classifier_ == BCC_SPECKLE_CLASSIFIER; } void set_unichar_id(UNICHAR_ID newunichar_id) { @@ -110,8 +140,9 @@ class BLOB_CHOICE: public ELIST_LINK void set_script(int newscript_id) { script_id_ = newscript_id; } - void set_language_model_state(void *language_model_state) { - language_model_state_ = language_model_state; + void set_matrix_cell(int col, int row) { + matrix_cell_.col = col; + matrix_cell_.row = row; } void set_xgap_before(inT16 gap) { xgap_before_ = gap; @@ -119,19 +150,39 @@ class BLOB_CHOICE: public ELIST_LINK void set_xgap_after(inT16 gap) { xgap_after_ = gap; } - void set_adapted(bool adapted) { - adapted_ = adapted; + void set_classifier(BlobChoiceClassifier classifier) { + classifier_ = classifier; } static BLOB_CHOICE* deep_copy(const BLOB_CHOICE* src) { BLOB_CHOICE* choice = new BLOB_CHOICE; *choice = *src; return choice; } - void print(const UNICHARSET *unicharset) { - tprintf("r%.2f c%.2f : %d %s", rating_, certainty_, unichar_id_, + // Returns true if *this and other agree on the baseline and x-height + // to within some tolerance based on a given estimate of the x-height. + bool PosAndSizeAgree(const BLOB_CHOICE& other, float x_height, + bool debug) const; + + void print(const UNICHARSET *unicharset) const { + tprintf("r%.2f c%.2f x[%g,%g]: %d %s", + rating_, certainty_, + min_xheight_, max_xheight_, unichar_id_, (unicharset == NULL) ? "" : unicharset->debug_str(unichar_id_).string()); } + void print_full() const { + print(NULL); + tprintf(" script=%d, font1=%d, font2=%d, yshift=%g, classifier=%d\n", + script_id_, fontinfo_id_, fontinfo_id2_, yshift_, classifier_); + } + // Sort function for sorting BLOB_CHOICEs in increasing order of rating. + static int SortByRating(const void *p1, const void *p2) { + const BLOB_CHOICE *bc1 = + *reinterpret_cast(p1); + const BLOB_CHOICE *bc2 = + *reinterpret_cast(p2); + return (bc1->rating_ < bc2->rating_) ? -1 : 1; + } private: UNICHAR_ID unichar_id_; // unichar id @@ -149,21 +200,26 @@ class BLOB_CHOICE: public ELIST_LINK // k is defined as above to normalize -klog p to the range [0, 1]. float certainty_; // absolute int script_id_; - // Stores language model information about this BLOB_CHOICE. Used during - // the segmentation search for BLOB_CHOICEs in BLOB_CHOICE_LISTs that are - // recorded in the ratings matrix. - // The pointer is owned/managed by the segmentation search. - void *language_model_state_; + // Holds the position of this choice in the ratings matrix. + // Used to location position in the matrix during path backtracking. + MATRIX_COORD matrix_cell_; inT16 xgap_before_; inT16 xgap_after_; // X-height range (in image pixels) that this classification supports. - inT16 min_xheight_; - inT16 max_xheight_; - bool adapted_; // true if this is a match from adapted templates + float min_xheight_; + float max_xheight_; + // yshift_ - The vertical distance (in image pixels) the character is + // shifted (up or down) from an acceptable y position. + float yshift_; + BlobChoiceClassifier classifier_; // What generated *this. }; // Make BLOB_CHOICE listable. -ELISTIZEH (BLOB_CHOICE) CLISTIZEH (BLOB_CHOICE_LIST) +ELISTIZEH(BLOB_CHOICE) + +// Return the BLOB_CHOICE in bc_list matching a given unichar_id, +// or NULL if there is no match. +BLOB_CHOICE *FindMatchingChoice(UNICHAR_ID char_id, BLOB_CHOICE_LIST *bc_list); // Permuter codes used in WERD_CHOICEs. enum PermuterType { @@ -180,11 +236,27 @@ enum PermuterType { USER_DAWG_PERM, // 10 FREQ_DAWG_PERM, // 11 COMPOUND_PERM, // 12 + + NUM_PERMUTER_TYPES }; -class WERD_CHOICE { +namespace tesseract { +// ScriptPos tells whether a character is subscript, superscript or normal. +enum ScriptPos { + SP_NORMAL, + SP_SUBSCRIPT, + SP_SUPERSCRIPT, + SP_DROPCAP +}; + +const char *ScriptPosToString(tesseract::ScriptPos script_pos); + +} // namespace tesseract. + +class WERD_CHOICE : public ELIST_LINK { public: static const float kBadRating; + static const char *permuter_name(uinT8 permuter); WERD_CHOICE(const UNICHARSET *unicharset) : unicharset_(unicharset) { this->init(8); } @@ -213,6 +285,12 @@ class WERD_CHOICE { inline int length() const { return length_; } + float adjust_factor() const { + return adjust_factor_; + } + void set_adjust_factor(float factor) { + adjust_factor_ = factor; + } inline const UNICHAR_ID *unichar_ids() const { return unichar_ids_; } @@ -220,12 +298,13 @@ class WERD_CHOICE { assert(index < length_); return unichar_ids_[index]; } - inline const char *fragment_lengths() const { - return fragment_lengths_; + inline int state(int index) const { + return state_[index]; } - inline const char fragment_length(int index) const { - assert(index < length_); - return fragment_lengths_[index]; + tesseract::ScriptPos BlobPosition(int index) const { + if (index < 0 || index >= length_) + return tesseract::SP_NORMAL; + return script_pos_[index]; } inline float rating() const { return rating_; @@ -233,23 +312,41 @@ class WERD_CHOICE { inline float certainty() const { return certainty_; } + inline float certainty(int index) const { + return certainties_[index]; + } + inline float min_x_height() const { + return min_x_height_; + } + inline float max_x_height() const { + return max_x_height_; + } + inline void set_x_heights(float min_height, float max_height) { + min_x_height_ = min_height; + max_x_height_ = max_height; + } inline uinT8 permuter() const { return permuter_; } const char *permuter_name() const; - inline bool fragment_mark() const { - return fragment_mark_; - } - inline BLOB_CHOICE_LIST_CLIST* blob_choices() { - return blob_choices_; - } + // Returns the BLOB_CHOICE_LIST corresponding to the given index in the word, + // taken from the appropriate cell in the ratings MATRIX. + // Borrowed pointer, so do not delete. + BLOB_CHOICE_LIST* blob_choices(int index, MATRIX* ratings) const; + + // Returns the MATRIX_COORD corresponding to the location in the ratings + // MATRIX for the given index into the word. + MATRIX_COORD MatrixCoord(int index) const; + inline void set_unichar_id(UNICHAR_ID unichar_id, int index) { assert(index < length_); unichar_ids_[index] = unichar_id; } - inline void set_fragment_length(char flen, int index) { - assert(index < length_); - fragment_lengths_[index] = flen; + bool dangerous_ambig_found() const { + return dangerous_ambig_found_; + } + void set_dangerous_ambig_found_(bool value) { + dangerous_ambig_found_ = value; } inline void set_rating(float new_val) { rating_ = new_val; @@ -260,9 +357,6 @@ class WERD_CHOICE { inline void set_permuter(uinT8 perm) { permuter_ = perm; } - inline void set_fragment_mark(bool new_fragment_mark) { - fragment_mark_ = new_fragment_mark; - } // Note: this function should only be used if all the fields // are populated manually with set_* functions (rather than // (copy)constructors and append_* functions). @@ -270,19 +364,24 @@ class WERD_CHOICE { ASSERT_HOST(reserved_ >= len); length_ = len; } - void set_blob_choices(BLOB_CHOICE_LIST_CLIST *blob_choices); /// Make more space in unichar_id_ and fragment_lengths_ arrays. inline void double_the_size() { if (reserved_ > 0) { unichar_ids_ = GenericVector::double_the_size_memcpy( reserved_, unichar_ids_); - fragment_lengths_ = GenericVector::double_the_size_memcpy( - reserved_, fragment_lengths_); + script_pos_ = GenericVector::double_the_size_memcpy( + reserved_, script_pos_); + state_ = GenericVector::double_the_size_memcpy( + reserved_, state_); + certainties_ = GenericVector::double_the_size_memcpy( + reserved_, certainties_); reserved_ *= 2; } else { unichar_ids_ = new UNICHAR_ID[1]; - fragment_lengths_ = new char[1]; + script_pos_ = new tesseract::ScriptPos[1]; + state_ = new int[1]; + certainties_ = new float[1]; reserved_ = 1; } } @@ -293,18 +392,24 @@ class WERD_CHOICE { reserved_ = reserved; if (reserved > 0) { unichar_ids_ = new UNICHAR_ID[reserved]; - fragment_lengths_ = new char[reserved]; + script_pos_ = new tesseract::ScriptPos[reserved]; + state_ = new int[reserved]; + certainties_ = new float[reserved]; } else { unichar_ids_ = NULL; - fragment_lengths_ = NULL; + script_pos_ = NULL; + state_ = NULL; + certainties_ = NULL; } length_ = 0; + adjust_factor_ = 1.0f; rating_ = 0.0; certainty_ = MAX_FLOAT32; + min_x_height_ = 0.0f; + max_x_height_ = MAX_FLOAT32; permuter_ = NO_PERM; - fragment_mark_ = false; - blob_choices_ = NULL; unichars_in_script_order_ = false; // Tesseract is strict left-to-right. + dangerous_ambig_found_ = false; } /// Helper function to build a WERD_CHOICE from the given string, @@ -321,34 +426,39 @@ class WERD_CHOICE { length_ = 0; rating_ = kBadRating; certainty_ = -MAX_FLOAT32; - fragment_mark_ = false; } /// This function assumes that there is enough space reserved /// in the WERD_CHOICE for adding another unichar. /// This is an efficient alternative to append_unichar_id(). inline void append_unichar_id_space_allocated( - UNICHAR_ID unichar_id, char fragment_length, + UNICHAR_ID unichar_id, int blob_count, float rating, float certainty) { assert(reserved_ > length_); length_++; - this->set_unichar_id(unichar_id, fragment_length, + this->set_unichar_id(unichar_id, blob_count, rating, certainty, length_-1); } - void append_unichar_id(UNICHAR_ID unichar_id, char fragment_length, + void append_unichar_id(UNICHAR_ID unichar_id, int blob_count, float rating, float certainty); - inline void set_unichar_id(UNICHAR_ID unichar_id, char fragment_length, + inline void set_unichar_id(UNICHAR_ID unichar_id, int blob_count, float rating, float certainty, int index) { assert(index < length_); unichar_ids_[index] = unichar_id; - fragment_lengths_[index] = fragment_length; + state_[index] = blob_count; + certainties_[index] = certainty; + script_pos_[index] = tesseract::SP_NORMAL; rating_ += rating; if (certainty < certainty_) { certainty_ = certainty; } } + // Sets the entries for the given index from the BLOB_CHOICE, assuming + // unit fragment lengths, but setting the state for this index to blob_count. + void set_blob_choice(int index, int blob_count, + const BLOB_CHOICE* blob_choice); bool contains_unichar_id(UNICHAR_ID unichar_id) const; void remove_unichar_ids(int index, int num); @@ -364,6 +474,11 @@ class WERD_CHOICE { // punctuation from the left and right. void punct_stripped(int *start_core, int *end_core) const; + // Returns the indices [start, end) containing the core of the word, stripped + // of any superscript digits on either side. (i.e., the non-footnote part + // of the word). There is no guarantee that the output range is non-empty. + void GetNonSuperscriptSpan(int *start, int *end) const; + // Return a copy of this WERD_CHOICE with the choices [start, end). // The result is useful only for checking against a dictionary. WERD_CHOICE shallow_copy(int start, int end) const; @@ -402,8 +517,42 @@ class WERD_CHOICE { this->string_and_lengths(&unichar_string_, &unichar_lengths_); return unichar_lengths_; } - const void print() const { this->print(""); } - const void print(const char *msg) const; + + // Sets up the script_pos_ member using the blobs_list to get the bln + // bounding boxes, *this to get the unichars, and this->unicharset + // to get the target positions. If small_caps is true, sub/super are not + // considered, but dropcaps are. + // NOTE: blobs_list should be the chopped_word blobs. (Fully segemented.) + void SetScriptPositions(bool small_caps, TWERD* word); + // Sets the script_pos_ member from some source positions with a given length. + void SetScriptPositions(const tesseract::ScriptPos* positions, int length); + // Sets all the script_pos_ positions to the given position. + void SetAllScriptPositions(tesseract::ScriptPos position); + + static tesseract::ScriptPos ScriptPositionOf(bool print_debug, + const UNICHARSET& unicharset, + const TBOX& blob_box, + UNICHAR_ID unichar_id); + + // Returns the "dominant" script ID for the word. By "dominant", the script + // must account for at least half the characters. Otherwise, it returns 0. + // Note that for Japanese, Hiragana and Katakana are simply treated as Han. + int GetTopScriptID() const; + + // Fixes the state_ for a chop at the given blob_posiiton. + void UpdateStateForSplit(int blob_position); + + // Returns the sum of all the state elements, being the total number of blobs. + int TotalOfStates() const; + + void print() const { this->print(""); } + void print(const char *msg) const; + // Prints the segmentation state with an introductory message. + void print_state(const char *msg) const; + + // Displays the segmentation state of *this (if not the same as the last + // one displayed) and waits for a click in the window. + void DisplaySegmentation(TWERD* word); WERD_CHOICE& operator+= ( // concatanate const WERD_CHOICE & second);// second on first @@ -412,41 +561,55 @@ class WERD_CHOICE { private: const UNICHARSET *unicharset_; + // TODO(rays) Perhaps replace the multiple arrays with an array of structs? + // unichar_ids_ is an array of classifier "results" that make up a word. + // For each unichar_ids_[i], script_pos_[i] has the sub/super/normal position + // of each unichar_id. + // state_[i] indicates the number of blobs in WERD_RES::chopped_word that + // were put together to make the classification results in the ith position + // in unichar_ids_, and certainties_[i] is the certainty of the choice that + // was used in this word. + // == Change from before == + // Previously there was fragment_lengths_ that allowed a word to be + // artificially composed of multiple fragment results. Since the new + // segmentation search doesn't do fragments, treatment of fragments has + // been moved to a lower level, augmenting the ratings matrix with the + // combined fragments, and allowing the language-model/segmentation-search + // to deal with only the combined unichar_ids. UNICHAR_ID *unichar_ids_; // unichar ids that represent the text of the word - char *fragment_lengths_; // number of fragments in each unichar + tesseract::ScriptPos* script_pos_; // Normal/Sub/Superscript of each unichar. + int* state_; // Number of blobs in each unichar. + float* certainties_; // Certainty of each unichar. int reserved_; // size of the above arrays int length_; // word length + // Factor that was used to adjust the rating. + float adjust_factor_; // Rating is the sum of the ratings of the individual blobs in the word. float rating_; // size related // certainty is the min (worst) certainty of the individual blobs in the word. float certainty_; // absolute + // xheight computed from the result, or 0 if inconsistent. + float min_x_height_; + float max_x_height_; uinT8 permuter_; // permuter code - bool fragment_mark_; // if true, indicates that this choice - // was chosen over a better one that - // contained a fragment - BLOB_CHOICE_LIST_CLIST *blob_choices_; // best choices for each blob - // Normally, the blob_choices_ represent the recognition results in order + // Normally, the ratings_ matrix represents the recognition results in order // from left-to-right. However, some engines (say Cube) may return // recognition results in the order of the script's major reading direction // (for Arabic, that is right-to-left). bool unichars_in_script_order_; + // True if NoDangerousAmbig found an ambiguity. + bool dangerous_ambig_found_; // The following variables are populated and passed by reference any // time unichar_string() or unichar_lengths() are called. mutable STRING unichar_string_; mutable STRING unichar_lengths_; - - bool unichar_info_present; - - private: - void delete_blob_choices(); }; // Make WERD_CHOICE listable. -ELISTIZEH (WERD_CHOICE) +ELISTIZEH(WERD_CHOICE) typedef GenericVector BLOB_CHOICE_LIST_VECTOR; -typedef GenericVector WERD_CHOICE_LIST_VECTOR; // Utilities for comparing WERD_CHOICEs @@ -454,27 +617,11 @@ bool EqualIgnoringCaseAndTerminalPunct(const WERD_CHOICE &word1, const WERD_CHOICE &word2); // Utilities for debug printing. -void print_ratings_list(const char *msg, BLOB_CHOICE_LIST *ratings); void print_ratings_list( const char *msg, // intro message BLOB_CHOICE_LIST *ratings, // list of results const UNICHARSET ¤t_unicharset // unicharset that can be used // for id-to-unichar conversion ); -void print_ratings_info( - FILE *fp, // file to use - BLOB_CHOICE_LIST *ratings, // list of results - const UNICHARSET ¤t_unicharset // unicharset that can be used - // for id-to-unichar conversion - ); -void print_char_choices_list( - const char *msg, - const BLOB_CHOICE_LIST_VECTOR &char_choices, - const UNICHARSET ¤t_unicharset, - BOOL8 detailed - ); -void print_word_alternates_list( - WERD_CHOICE *word, - GenericVector *alternates); #endif diff --git a/ccstruct/rect.cpp b/ccstruct/rect.cpp index 5d384cae33..1125f55689 100644 --- a/ccstruct/rect.cpp +++ b/ccstruct/rect.cpp @@ -171,6 +171,16 @@ void TBOX::plot( //paint box } #endif +// Appends the bounding box as (%d,%d)->(%d,%d) to a STRING. +void TBOX::print_to_str(STRING *str) const { + // "(%d,%d)->(%d,%d)", left(), bottom(), right(), top() + str->add_str_int("(", left()); + str->add_str_int(",", bottom()); + str->add_str_int(")->(", right()); + str->add_str_int(",", top()); + *str += ')'; +} + // Writes to the given file. Returns false in case of error. bool TBOX::Serialize(FILE* fp) const { if (!bot_left.Serialize(fp)) return false; diff --git a/ccstruct/rect.h b/ccstruct/rect.h index 2682ad9f86..534656e025 100644 --- a/ccstruct/rect.h +++ b/ccstruct/rect.h @@ -24,6 +24,7 @@ #include "points.h" #include "ndminx.h" #include "scrollview.h" +#include "strngs.h" #include "tprintf.h" class DLLSYM TBOX { // bounding box @@ -264,15 +265,8 @@ class DLLSYM TBOX { // bounding box tprintf("Bounding box=(%d,%d)->(%d,%d)\n", left(), bottom(), right(), top()); } - - // Same as print(), but appends debug information to the given string - // instead of printing it to stdout. - void append_debug(STRING *str) const { - char buffer[256]; - sprintf(buffer, "Bounding box=(%d,%d)->(%d,%d)\n", - left(), bottom(), right(), top()); - *str += buffer; - } + // Appends the bounding box as (%d,%d)->(%d,%d) to a STRING. + void print_to_str(STRING *str) const; #ifndef GRAPHICS_DISABLED void plot( // use current settings diff --git a/ccstruct/seam.cpp b/ccstruct/seam.cpp index 41219c99c0..d4ccd372d9 100644 --- a/ccstruct/seam.cpp +++ b/ccstruct/seam.cpp @@ -27,8 +27,8 @@ ----------------------------------------------------------------------*/ #include "seam.h" #include "blobs.h" -#include "callcpp.h" -#include "structures.h" +#include "freelist.h" +#include "tprintf.h" #ifdef __UNIX__ #include @@ -38,7 +38,6 @@ V a r i a b l e s ----------------------------------------------------------------------*/ #define NUM_STARTING_SEAMS 20 -makestructure(newseam, free_seam, SEAM); /*---------------------------------------------------------------------- Public Function Code @@ -66,7 +65,7 @@ bool point_in_split(SPLIT *split, EDGEPT *point1, EDGEPT *point2) { * seam. * @returns TRUE if one of them is. */ -bool point_in_seam(SEAM *seam, SPLIT *split) { +bool point_in_seam(const SEAM *seam, SPLIT *split) { return (point_in_split(seam->split1, split->point1, split->point2) || point_in_split(seam->split2, split->point1, split->point2) || point_in_split(seam->split3, split->point1, split->point2)); @@ -96,16 +95,6 @@ bool point_used_by_seam(SEAM *seam, EDGEPT *point) { point_used_by_split(seam->split3, point); } -/** - * @name add_seam - * - * Add another seam to a collection of seams. - */ -SEAMS add_seam(SEAMS seam_list, SEAM *seam) { - return (array_push (seam_list, seam)); -} - - /** * @name combine_seam * @@ -126,7 +115,8 @@ void combine_seams(SEAM *dest_seam, SEAM *source_seam) { else if (!dest_seam->split3) dest_seam->split3 = source_seam->split1; else - cprintf("combine_seam: Seam is too crowded, can't be combined !\n"); + delete source_seam->split1; // Wouldn't have fitted. + source_seam->split1 = NULL; } if (source_seam->split2) { if (!dest_seam->split2) @@ -134,35 +124,17 @@ void combine_seams(SEAM *dest_seam, SEAM *source_seam) { else if (!dest_seam->split3) dest_seam->split3 = source_seam->split2; else - cprintf("combine_seam: Seam is too crowded, can't be combined !\n"); + delete source_seam->split2; // Wouldn't have fitted. + source_seam->split2 = NULL; } if (source_seam->split3) { if (!dest_seam->split3) dest_seam->split3 = source_seam->split3; else - cprintf("combine_seam: Seam is too crowded, can't be combined !\n"); - } - free_seam(source_seam); -} - - -/** - * @name delete_seam - * - * Free this seam record and the splits that are attached to it. - */ -void delete_seam(void *arg) { //SEAM *seam) - SEAM *seam = (SEAM *) arg; - - if (seam) { - if (seam->split1) - delete_split(seam->split1); - if (seam->split2) - delete_split(seam->split2); - if (seam->split3) - delete_split(seam->split3); - free_seam(seam); + delete source_seam->split3; // Wouldn't have fitted. + source_seam->split3 = NULL; } + delete source_seam; } /** @@ -172,36 +144,17 @@ void delete_seam(void *arg) { //SEAM *seam) * present in the starting segmentation. Each of the seams created * by this routine have location information only. */ -SEAMS start_seam_list(TBLOB *blobs) { - TBLOB *blob; - SEAMS seam_list; +void start_seam_list(TWERD *word, GenericVector* seam_array) { + seam_array->truncate(0); TPOINT location; - /* Seam slot per char */ - seam_list = new_seam_list (); - for (blob = blobs; blob->next != NULL; blob = blob->next) { - TBOX bbox = blob->bounding_box(); - TBOX nbox = blob->next->bounding_box(); + for (int b = 1; b < word->NumBlobs(); ++b) { + TBOX bbox = word->blobs[b - 1]->bounding_box(); + TBOX nbox = word->blobs[b]->bounding_box(); location.x = (bbox.right() + nbox.left()) / 2; location.y = (bbox.bottom() + bbox.top() + nbox.bottom() + nbox.top()) / 4; - seam_list = add_seam(seam_list, - new_seam(0.0, location, NULL, NULL, NULL)); + seam_array->push_back(new SEAM(0.0f, location, NULL, NULL, NULL)); } - - return seam_list; -} - -/** - * @name free_seam_list - * - * Free all the seams that have been allocated in this list. Reclaim - * the memory for each of the splits as well. - */ -void free_seam_list(SEAMS seam_list) { - int x; - - array_loop(seam_list, x) delete_seam(array_value (seam_list, x)); - array_free(seam_list); } @@ -210,32 +163,26 @@ void free_seam_list(SEAMS seam_list) { * * @returns true if insert_seam will succeed. */ -bool test_insert_seam(SEAMS seam_list, - int index, - TBLOB *left_blob, - TBLOB *first_blob) { +bool test_insert_seam(const GenericVector& seam_array, + TWERD *word, int index) { SEAM *test_seam; TBLOB *blob; int test_index; int list_length; - list_length = array_count (seam_list); - for (test_index=0, blob=first_blob->next; - test_index < index; - test_index++, blob=blob->next) { - test_seam = (SEAM *) array_value(seam_list, test_index); + list_length = seam_array.size(); + for (int test_index = 0; test_index < index; ++test_index) { + test_seam = seam_array[test_index]; if (test_index + test_seam->widthp < index && test_seam->widthp + test_index == index - 1 && - account_splits_right(test_seam, blob) < 0) + account_splits(test_seam, word, test_index + 1, 1) < 0) return false; } - for (test_index=index, blob=left_blob->next; - test_index < list_length; - test_index++, blob=blob->next) { - test_seam = (SEAM *) array_value(seam_list, test_index); + for (int test_index = index; test_index < list_length; test_index++) { + test_seam = seam_array[test_index]; if (test_index - test_seam->widthn >= index && test_index - test_seam->widthn == index && - account_splits_left(test_seam, first_blob, blob) < 0) + account_splits(test_seam, word, test_index + 1, -1) < 0) return false; } return true; @@ -247,58 +194,51 @@ bool test_insert_seam(SEAMS seam_list, * Add another seam to a collection of seams at a particular location * in the seam array. */ -SEAMS insert_seam(SEAMS seam_list, - int index, - SEAM *seam, - TBLOB *left_blob, - TBLOB *first_blob) { +void insert_seam(const TWERD* word, int index, SEAM *seam, + GenericVector* seam_array) { SEAM *test_seam; - TBLOB *blob; int test_index; int list_length; - list_length = array_count(seam_list); - for (test_index=0, blob=first_blob->next; - test_index < index; - test_index++, blob=blob->next) { - test_seam = (SEAM *) array_value(seam_list, test_index); + list_length = seam_array->size(); + for (int test_index = 0; test_index < index; ++test_index) { + test_seam = seam_array->get(test_index); if (test_index + test_seam->widthp >= index) { test_seam->widthp++; /*got in the way */ } else if (test_seam->widthp + test_index == index - 1) { - test_seam->widthp = account_splits_right(test_seam, blob); + test_seam->widthp = account_splits(test_seam, word, test_index + 1, 1); if (test_seam->widthp < 0) { - cprintf("Failed to find any right blob for a split!\n"); + tprintf("Failed to find any right blob for a split!\n"); print_seam("New dud seam", seam); print_seam("Failed seam", test_seam); } } } - for (test_index=index, blob=left_blob->next; - test_index < list_length; - test_index++, blob=blob->next) { - test_seam = (SEAM *) array_value(seam_list, test_index); + for (int test_index = index; test_index < list_length; test_index++) { + test_seam = seam_array->get(test_index); if (test_index - test_seam->widthn < index) { test_seam->widthn++; /*got in the way */ } else if (test_index - test_seam->widthn == index) { - test_seam->widthn = account_splits_left(test_seam, first_blob, blob); + test_seam->widthn = account_splits(test_seam, word, test_index + 1, -1); if (test_seam->widthn < 0) { - cprintf("Failed to find any left blob for a split!\n"); + tprintf("Failed to find any left blob for a split!\n"); print_seam("New dud seam", seam); print_seam("Failed seam", test_seam); } } } - return (array_insert (seam_list, index, seam)); + seam_array->insert(seam, index); } /** - * @name account_splits_right + * @name account_splits * - * Account for all the splits by looking to the right. - * in the blob list. + * Account for all the splits by looking to the right (blob_direction == 1), + * or to the left (blob_direction == -1) in the word. */ -int account_splits_right(SEAM *seam, TBLOB *blob) { +int account_splits(const SEAM *seam, const TWERD *word, int blob_index, + int blob_direction) { inT8 found_em[3]; inT8 width; @@ -309,6 +249,7 @@ int account_splits_right(SEAM *seam, TBLOB *blob) { return 0; width = 0; do { + TBLOB* blob = word->blobs[blob_index]; if (!found_em[0]) found_em[0] = find_split_in_blob(seam->split1, blob); if (!found_em[1]) @@ -319,54 +260,12 @@ int account_splits_right(SEAM *seam, TBLOB *blob) { return width; } width++; - blob = blob->next; - } while (blob != NULL); + blob_index += blob_direction; + } while (0 <= blob_index && blob_index < word->NumBlobs()); return -1; } -/** - * @name account_splits_left - * - * Account for all the splits by looking to the left. - * in the blob list. - */ -int account_splits_left(SEAM *seam, TBLOB *blob, TBLOB *end_blob) { - inT32 depth = 0; - inT8 width = 0; - inT8 found_em[3]; - account_splits_left_helper(seam, blob, end_blob, &depth, &width, found_em); - return width; -} - -void account_splits_left_helper(SEAM *seam, TBLOB *blob, TBLOB *end_blob, - inT32 *depth, inT8 *width, inT8* found_em) { - if (blob != end_blob) { - (*depth)++; - account_splits_left_helper(seam, blob->next, end_blob, - depth, width, found_em); - (*depth)--; - } else { - found_em[0] = seam->split1 == NULL; - found_em[1] = seam->split2 == NULL; - found_em[2] = seam->split3 == NULL; - *width = 0; - } - if (!found_em[0]) - found_em[0] = find_split_in_blob(seam->split1, blob); - if (!found_em[1]) - found_em[1] = find_split_in_blob(seam->split2, blob); - if (!found_em[2]) - found_em[2] = find_split_in_blob(seam->split3, blob); - if (!found_em[0] || !found_em[1] || !found_em[2]) { - (*width)++; - if (*depth == 0) { - *width = -1; - } - } -} - - /** * @name find_split_in_blob * @@ -393,7 +292,7 @@ bool find_split_in_blob(SPLIT *split, TBLOB *blob) { * Merge these two seams into a new seam. Duplicate the split records * in both of the input seams. Return the resultant seam. */ -SEAM *join_two_seams(SEAM *seam1, SEAM *seam2) { +SEAM *join_two_seams(const SEAM *seam1, const SEAM *seam2) { SEAM *result = NULL; SEAM *temp; @@ -403,52 +302,13 @@ SEAM *join_two_seams(SEAM *seam1, SEAM *seam2) { (seam1->split2 == NULL && seam2->split3 == NULL) || seam1->split1 == NULL || seam2->split1 == NULL) && (!shared_split_points(seam1, seam2))) { - clone_seam(result, seam1); - clone_seam(temp, seam2); + result = new SEAM(*seam1); + temp = new SEAM(*seam2); combine_seams(result, temp); } return (result); } - -/** - * @name new_seam - * - * Create a structure for a "seam" between two blobs. This data - * structure may actually hold up to three different splits. - * Initailization of this record is done by this routine. - */ -SEAM *new_seam(PRIORITY priority, - const TPOINT& location, - SPLIT *split1, - SPLIT *split2, - SPLIT *split3) { - SEAM *seam; - - seam = newseam (); - - seam->priority = priority; - seam->location = location; - seam->widthp = 0; - seam->widthn = 0; - seam->split1 = split1; - seam->split2 = split2; - seam->split3 = split3; - - return (seam); -} - - -/** - * @name new_seam_list - * - * Create a collection of seam records in an array. - */ -SEAMS new_seam_list() { - return (array_new (NUM_STARTING_SEAMS)); -} - - /** * @name print_seam * @@ -457,21 +317,21 @@ SEAMS new_seam_list() { */ void print_seam(const char *label, SEAM *seam) { if (seam) { - cprintf(label); - cprintf(" %6.2f @ (%d,%d), p=%d, n=%d ", + tprintf(label); + tprintf(" %6.2f @ (%d,%d), p=%d, n=%d ", seam->priority, seam->location.x, seam->location.y, seam->widthp, seam->widthn); print_split(seam->split1); if (seam->split2) { - cprintf(", "); + tprintf(", "); print_split (seam->split2); if (seam->split3) { - cprintf(", "); + tprintf(", "); print_split (seam->split3); } } - cprintf ("\n"); + tprintf("\n"); } } @@ -482,17 +342,16 @@ void print_seam(const char *label, SEAM *seam) { * Print a list of splits. Show the coordinates of both points in * each split. */ -void print_seams(const char *label, SEAMS seams) { - int x; +void print_seams(const char *label, const GenericVector& seams) { char number[CHARS_PER_LINE]; - if (seams) { - cprintf("%s\n", label); - array_loop(seams, x) { + if (!seams.empty()) { + tprintf("%s\n", label); + for (int x = 0; x < seams.size(); ++x) { sprintf(number, "%2d: ", x); - print_seam(number, (SEAM *) array_value(seams, x)); + print_seam(number, seams[x]); } - cprintf("\n"); + tprintf("\n"); } } @@ -504,7 +363,7 @@ void print_seams(const char *label, SEAMS seams) { * points in common. Return TRUE if any of the same points are present * in any of the splits of both seams. */ -int shared_split_points(SEAM *seam1, SEAM *seam2) { +int shared_split_points(const SEAM *seam1, const SEAM *seam2) { if (seam1 == NULL || seam2 == NULL) return (FALSE); @@ -532,23 +391,20 @@ int shared_split_points(SEAM *seam1, SEAM *seam2) { * Break up the blobs in this chain so that they are all independent. * This operation should undo the affect of join_pieces. **********************************************************************/ -void break_pieces(TBLOB *blobs, SEAMS seams, inT16 start, inT16 end) { - TESSLINE *outline = blobs->outlines; - TBLOB *next_blob; - inT16 x; +void break_pieces(const GenericVector& seams, int first, int last, + TWERD *word) { + for (int x = first; x < last; ++x) + reveal_seam(seams[x]); - for (x = start; x < end; x++) - reveal_seam ((SEAM *) array_value (seams, x)); + TESSLINE *outline = word->blobs[first]->outlines; + int next_blob = first + 1; - next_blob = blobs->next; - - while (outline && next_blob) { - if (outline->next == next_blob->outlines) { + while (outline != NULL && next_blob <= last) { + if (outline->next == word->blobs[next_blob]->outlines) { outline->next = NULL; - outline = next_blob->outlines; - next_blob = next_blob->next; - } - else { + outline = word->blobs[next_blob]->outlines; + ++next_blob; + } else { outline = outline->next; } } @@ -561,30 +417,19 @@ void break_pieces(TBLOB *blobs, SEAMS seams, inT16 start, inT16 end) { * Join a group of base level pieces into a single blob that can then * be classified. **********************************************************************/ -void join_pieces(TBLOB *piece_blobs, SEAMS seams, inT16 start, inT16 end) { - TBLOB *next_blob; - TBLOB *blob; - inT16 x; - TESSLINE *outline; - SEAM *seam; - - for (x = 0, blob = piece_blobs; x < start; x++) - blob = blob->next; - next_blob = blob->next; - outline = blob->outlines; +void join_pieces(const GenericVector& seams, int first, int last, + TWERD *word) { + TESSLINE *outline = word->blobs[first]->outlines; if (!outline) return; - while (x < end) { - seam = (SEAM *) array_value (seams, x); - if (x - seam->widthn >= start && x + seam->widthp < end) + for (int x = first; x < last; ++x) { + SEAM *seam = seams[x]; + if (x - seam->widthn >= first && x + seam->widthp < last) hide_seam(seam); while (outline->next) outline = outline->next; - outline->next = next_blob->outlines; - next_blob = next_blob->next; - - x++; + outline->next = word->blobs[x + 1]->outlines; } } @@ -626,7 +471,7 @@ void hide_edge_pair(EDGEPT *pt1, EDGEPT *pt2) { } while (!exact_point (edgept, pt2) && edgept != pt1); if (edgept == pt1) { - /* cprintf("Hid entire outline at (%d,%d)!!\n", + /* tprintf("Hid entire outline at (%d,%d)!!\n", edgept->pos.x,edgept->pos.y); */ } edgept = pt2; @@ -636,7 +481,7 @@ void hide_edge_pair(EDGEPT *pt1, EDGEPT *pt2) { } while (!exact_point (edgept, pt1) && edgept != pt2); if (edgept == pt2) { - /* cprintf("Hid entire outline at (%d,%d)!!\n", + /* tprintf("Hid entire outline at (%d,%d)!!\n", edgept->pos.x,edgept->pos.y); */ } } @@ -679,7 +524,7 @@ void reveal_edge_pair(EDGEPT *pt1, EDGEPT *pt2) { } while (!exact_point (edgept, pt2) && edgept != pt1); if (edgept == pt1) { - /* cprintf("Hid entire outline at (%d,%d)!!\n", + /* tprintf("Hid entire outline at (%d,%d)!!\n", edgept->pos.x,edgept->pos.y); */ } edgept = pt2; @@ -689,7 +534,7 @@ void reveal_edge_pair(EDGEPT *pt1, EDGEPT *pt2) { } while (!exact_point (edgept, pt1) && edgept != pt2); if (edgept == pt2) { - /* cprintf("Hid entire outline at (%d,%d)!!\n", + /* tprintf("Hid entire outline at (%d,%d)!!\n", edgept->pos.x,edgept->pos.y); */ } } diff --git a/ccstruct/seam.h b/ccstruct/seam.h index 2bef37fc55..23b7bc71f5 100644 --- a/ccstruct/seam.h +++ b/ccstruct/seam.h @@ -30,15 +30,36 @@ ----------------------------------------------------------------------*/ #include "blobs.h" #include "split.h" -#include "tessarray.h" /*---------------------------------------------------------------------- T y p e s ----------------------------------------------------------------------*/ typedef float PRIORITY; /* PRIORITY */ -typedef struct seam_record -{ /* SEAM */ +struct SEAM { + // Constructor that was formerly new_seam. + SEAM(PRIORITY priority0, const TPOINT& location0, + SPLIT *splita, SPLIT *splitb, SPLIT *splitc) + : priority(priority0), widthp(0), widthn(0), location(location0), + split1(splita), split2(splitb), split3(splitc) {} + // Copy constructor that was formerly clone_seam. + SEAM(const SEAM& src) + : priority(src.priority), widthp(src.widthp), widthn(src.widthn), + location(src.location) { + clone_split(split1, src.split1); + clone_split(split2, src.split2); + clone_split(split3, src.split3); + } + // Destructor was delete_seam. + ~SEAM() { + if (split1) + delete_split(split1); + if (split2) + delete_split(split2); + if (split3) + delete_split(split3); + } + PRIORITY priority; inT8 widthp; inT8 widthn; @@ -46,36 +67,7 @@ typedef struct seam_record SPLIT *split1; SPLIT *split2; SPLIT *split3; -} SEAM; - -typedef ARRAY SEAMS; /* SEAMS */ - -extern SEAM *newseam(); - -/*---------------------------------------------------------------------- - M a c r o s -----------------------------------------------------------------------*/ -/** - * @name clone_seam - * - * Create a new seam record and copy the contents of this seam into it. - */ - -#define clone_seam(dest,source) \ -if (source) { \ - (dest) = newseam (); \ - (dest)->location = (source)->location; \ - (dest)->widthp = (source)->widthp; \ - (dest)->widthn = (source)->widthn; \ - (dest)->priority = (source)->priority; \ - clone_split ((dest)->split1, (source)->split1); \ - clone_split ((dest)->split2, (source)->split2); \ - clone_split ((dest)->split3, (source)->split3); \ -} \ -else { \ - (dest) = (SEAM*) NULL; \ -} \ - +}; /** * exact_point @@ -92,61 +84,40 @@ else { \ ----------------------------------------------------------------------*/ bool point_in_split(SPLIT *split, EDGEPT *point1, EDGEPT *point2); -bool point_in_seam(SEAM *seam, SPLIT *split); +bool point_in_seam(const SEAM *seam, SPLIT *split); bool point_used_by_split(SPLIT *split, EDGEPT *point); bool point_used_by_seam(SEAM *seam, EDGEPT *point); -SEAMS add_seam(SEAMS seam_list, SEAM *seam); - void combine_seams(SEAM *dest_seam, SEAM *source_seam); -void delete_seam(void *arg); //SEAM *seam); - -SEAMS start_seam_list(TBLOB *blobs); - -void free_seam_list(SEAMS seam_list); +void start_seam_list(TWERD *word, GenericVector* seam_array); -bool test_insert_seam(SEAMS seam_list, - int index, - TBLOB *left_blob, - TBLOB *first_blob); +bool test_insert_seam(const GenericVector& seam_array, + TWERD *word, int index); -SEAMS insert_seam(SEAMS seam_list, - int index, - SEAM *seam, - TBLOB *left_blob, - TBLOB *first_blob); +void insert_seam(const TWERD *word, int index, SEAM *seam, + GenericVector* seam_array); -int account_splits_right(SEAM *seam, TBLOB *blob); - -int account_splits_left(SEAM *seam, TBLOB *blob, TBLOB *end_blob); - -void account_splits_left_helper(SEAM *seam, TBLOB *blob, TBLOB *end_blob, - inT32 *depth, inT8 *width, inT8 *found_em); +int account_splits(const SEAM *seam, const TWERD *word, int blob_index, + int blob_direction); bool find_split_in_blob(SPLIT *split, TBLOB *blob); -SEAM *join_two_seams(SEAM *seam1, SEAM *seam2); - -SEAM *new_seam(PRIORITY priority, - const TPOINT& location, - SPLIT *split1, - SPLIT *split2, - SPLIT *split3); - -SEAMS new_seam_list(); +SEAM *join_two_seams(const SEAM *seam1, const SEAM *seam2); void print_seam(const char *label, SEAM *seam); -void print_seams(const char *label, SEAMS seams); +void print_seams(const char *label, const GenericVector& seams); -int shared_split_points(SEAM *seam1, SEAM *seam2); +int shared_split_points(const SEAM *seam1, const SEAM *seam2); -void break_pieces(TBLOB *blobs, SEAMS seams, inT16 start, inT16 end); +void break_pieces(const GenericVector& seams, + int first, int last, TWERD *word); -void join_pieces(TBLOB *piece_blobs, SEAMS seams, inT16 start, inT16 end); +void join_pieces(const GenericVector& seams, + int first, int last, TWERD *word); void hide_seam(SEAM *seam); diff --git a/ccstruct/split.cpp b/ccstruct/split.cpp index c8404ce443..a2e974ef1c 100644 --- a/ccstruct/split.cpp +++ b/ccstruct/split.cpp @@ -26,8 +26,8 @@ I n c l u d e s ----------------------------------------------------------------------*/ #include "split.h" -#include "structures.h" -#include "callcpp.h" +#include "coutln.h" +#include "tprintf.h" #ifdef __UNIX__ #include @@ -38,8 +38,6 @@ ----------------------------------------------------------------------*/ BOOL_VAR(wordrec_display_splits, 0, "Display splits"); -makestructure(newsplit, free_split, SPLIT); - /*---------------------------------------------------------------------- F u n c t i o n s ----------------------------------------------------------------------*/ @@ -47,12 +45,11 @@ makestructure(newsplit, free_split, SPLIT); /********************************************************************** * delete_split * - * Remove this split from existance. Take if off the display list and - * deallocate its memory. + * Remove this split from existence. **********************************************************************/ void delete_split(SPLIT *split) { if (split) { - free_split(split); + delete split; } } @@ -68,6 +65,43 @@ EDGEPT *make_edgept(int x, int y, EDGEPT *next, EDGEPT *prev) { this_edgept = new EDGEPT; this_edgept->pos.x = x; this_edgept->pos.y = y; + // Now deal with the src_outline steps. + C_OUTLINE* prev_ol = prev->src_outline; + if (prev_ol != NULL && prev->next == next) { + // Compute the fraction of the segment that is being cut. + FCOORD segment_vec(next->pos.x - prev->pos.x, next->pos.y - prev->pos.y); + FCOORD target_vec(x - prev->pos.x, y - prev->pos.y); + double cut_fraction = target_vec.length() / segment_vec.length(); + // Get the start and end at the step level. + ICOORD step_start = prev_ol->position_at_index(prev->start_step); + int end_step = prev->start_step + prev->step_count; + int step_length = prev_ol->pathlength(); + ICOORD step_end = prev_ol->position_at_index(end_step % step_length); + ICOORD step_vec = step_end - step_start; + double target_length = step_vec.length() * cut_fraction; + // Find the point on the segment that gives the length nearest to target. + int best_step = prev->start_step; + ICOORD total_step(0, 0); + double best_dist = target_length; + for (int s = prev->start_step; s < end_step; ++s) { + total_step += prev_ol->step(s % step_length); + double dist = fabs(target_length - total_step.length()); + if (dist < best_dist) { + best_dist = dist; + best_step = s + 1; + } + } + // The new point is an intermediate point. + this_edgept->src_outline = prev_ol; + this_edgept->step_count = end_step - best_step; + this_edgept->start_step = best_step % step_length; + prev->step_count = best_step - prev->start_step; + } else { + // The new point is poly only. + this_edgept->src_outline = NULL; + this_edgept->step_count = 0; + this_edgept->start_step = 0; + } /* Hook it up */ this_edgept->next = next; this_edgept->prev = prev; @@ -78,8 +112,7 @@ EDGEPT *make_edgept(int x, int y, EDGEPT *next, EDGEPT *prev) { this_edgept->vec.y = this_edgept->next->pos.y - y; this_edgept->prev->vec.x = x - this_edgept->prev->pos.x; this_edgept->prev->vec.y = y - this_edgept->prev->pos.y; - - return (this_edgept); + return this_edgept; } /********************************************************************** @@ -90,6 +123,10 @@ EDGEPT *make_edgept(int x, int y, EDGEPT *next, EDGEPT *prev) { void remove_edgept(EDGEPT *point) { EDGEPT *prev = point->prev; EDGEPT *next = point->next; + // Add point's steps onto prev's steps if they are from the same outline. + if (prev->src_outline == point->src_outline && prev->src_outline != NULL) { + prev->step_count += point->step_count; + } prev->next = next; next->prev = prev; prev->vec.x = next->pos.x - prev->pos.x; @@ -104,8 +141,7 @@ void remove_edgept(EDGEPT *point) { * list. **********************************************************************/ SPLIT *new_split(EDGEPT *point1, EDGEPT *point2) { - SPLIT *s; - s = (SPLIT *) newsplit (); + SPLIT *s = new SPLIT; s->point1 = point1; s->point2 = point2; return (s); @@ -120,9 +156,9 @@ SPLIT *new_split(EDGEPT *point1, EDGEPT *point2) { **********************************************************************/ void print_split(SPLIT *split) { if (split) { - cprintf ("(%d,%d)--(%d,%d)", - split->point1->pos.x, split->point1->pos.y, - split->point2->pos.x, split->point2->pos.y); + tprintf("(%d,%d)--(%d,%d)", + split->point1->pos.x, split->point1->pos.y, + split->point2->pos.x, split->point2->pos.y); } } @@ -130,23 +166,35 @@ void print_split(SPLIT *split) { /********************************************************************** * split_outline * - * Split between these two edge points. Apply a split and return a - * pointer to the other side of the split. + * Split between these two edge points. **********************************************************************/ void split_outline(EDGEPT *join_point1, EDGEPT *join_point2) { - EDGEPT *join_point1a; - EDGEPT *temp2; - EDGEPT *temp1; - - assert (join_point1 != join_point2); + assert(join_point1 != join_point2); - temp2 = join_point2->next; - temp1 = join_point1->next; + EDGEPT* temp2 = join_point2->next; + EDGEPT* temp1 = join_point1->next; /* Create two new points */ - join_point1a = make_edgept (join_point1->pos.x, - join_point1->pos.y, temp1, join_point2); - - make_edgept (join_point2->pos.x, join_point2->pos.y, temp2, join_point1); + EDGEPT* new_point1 = make_edgept(join_point1->pos.x, join_point1->pos.y, + temp1, join_point2); + EDGEPT* new_point2 = make_edgept(join_point2->pos.x, join_point2->pos.y, + temp2, join_point1); + // Join_point1 and 2 are now cross-over points, so they must have NULL + // src_outlines and give their src_outline information their new + // replacements. + new_point1->src_outline = join_point1->src_outline; + new_point1->start_step = join_point1->start_step; + new_point1->step_count = join_point1->step_count; + new_point2->src_outline = join_point2->src_outline; + new_point2->start_step = join_point2->start_step; + new_point2->step_count = join_point2->step_count; + join_point1->src_outline = NULL; + join_point1->start_step = 0; + join_point1->step_count = 0; + join_point2->src_outline = NULL; + join_point2->start_step = 0; + join_point2->step_count = 0; + join_point1->MarkChop(); + join_point2->MarkChop(); } @@ -164,8 +212,18 @@ void unsplit_outlines(EDGEPT *p1, EDGEPT *p2) { tmp1->next->prev = p2; tmp2->next->prev = p1; + // tmp2 is coincident with p1. p1 takes tmp2's place as tmp2 is deleted. p1->next = tmp2->next; + p1->src_outline = tmp2->src_outline; + p1->start_step = tmp2->start_step; + p1->step_count = tmp2->step_count; + // Likewise p2 takes tmp1's place. p2->next = tmp1->next; + p2->src_outline = tmp1->src_outline; + p2->start_step = tmp1->start_step; + p2->step_count = tmp1->step_count; + p1->UnmarkChop(); + p2->UnmarkChop(); delete tmp1; delete tmp2; diff --git a/ccstruct/vecfuncs.h b/ccstruct/vecfuncs.h index 4a21d7c5e2..1179926aae 100644 --- a/ccstruct/vecfuncs.h +++ b/ccstruct/vecfuncs.h @@ -42,8 +42,7 @@ class EDGEPT; #define point_diff(p,p1,p2) \ ((p).x = (p1).x - (p2).x, \ - (p).y = (p1).y - (p2).y, \ - (p)) + (p).y = (p1).y - (p2).y) /********************************************************************** * CROSS diff --git a/ccstruct/werd.cpp b/ccstruct/werd.cpp index e58dbce3ac..150f6379f3 100644 --- a/ccstruct/werd.cpp +++ b/ccstruct/werd.cpp @@ -465,7 +465,7 @@ WERD* WERD::ConstructWerdWithNewBlobs(C_BLOB_LIST* all_blobs, TBOX a_blob_box = a_blob->bounding_box(); if ((not_found_box.major_overlap(a_blob_box) || a_blob_box.major_overlap(not_found_box)) && - not_found_box.y_overlap(a_blob_box)) { + not_found_box.y_overlap(a_blob_box) > 0.8) { // Already taken care of. delete not_found_it.extract(); break; diff --git a/ccutil/Makefile.am b/ccutil/Makefile.am index 647d499c48..da2f67b917 100644 --- a/ccutil/Makefile.am +++ b/ccutil/Makefile.am @@ -10,18 +10,16 @@ AM_CXXFLAGS += -fvisibility=hidden -fvisibility-inlines-hidden AM_CPPFLAGS += -DTESS_EXPORTS endif -EXTRA_DIST = mfcpch.cpp - include_HEADERS = \ basedir.h errcode.h fileerr.h genericvector.h helpers.h host.h memry.h \ ndminx.h params.h ocrclass.h platform.h serialis.h strngs.h \ tesscallback.h unichar.h unicharmap.h unicharset.h noinst_HEADERS = \ - ambigs.h bits16.h bitvector.h ccutil.h clst.h elst2.h \ - elst.h globaloc.h hashfn.h indexmapbidi.h lsterr.h \ - nwmain.h qrsequence.h secname.h sorthelper.h stderr.h tessdatamanager.h \ - tprintf.h unicity_table.h unicodes.h + ambigs.h bits16.h bitvector.h ccutil.h clst.h doubleptr.h elst2.h \ + elst.h genericheap.h globaloc.h hashfn.h indexmapbidi.h kdpair.h lsterr.h \ + nwmain.h object_cache.h qrsequence.h secname.h sorthelper.h stderr.h tessdatamanager.h \ + tprintf.h unicity_table.h unicodes.h universalambigs.h if !USING_MULTIPLELIBS noinst_LTLIBRARIES = libtesseract_ccutil.la @@ -39,7 +37,7 @@ libtesseract_ccutil_la_SOURCES = \ serialis.cpp strngs.cpp \ tessdatamanager.cpp tprintf.cpp \ unichar.cpp unicharmap.cpp unicharset.cpp unicodes.cpp \ - params.cpp + params.cpp universalambigs.cpp if EMBEDDED include_HEADERS += scanutils.h @@ -50,4 +48,4 @@ if MINGW AM_CPPFLAGS += -I$(top_srcdir)/vs2008/port -DWINDLLNAME=\"lib@GENERIC_LIBRARY_NAME@\" noinst_HEADERS += ../vs2008/port/strtok_r.h libtesseract_ccutil_la_SOURCES += ../vs2008/port/strtok_r.cpp -endif \ No newline at end of file +endif diff --git a/ccutil/ambigs.cpp b/ccutil/ambigs.cpp index 345081fce0..fbccdbdd0e 100644 --- a/ccutil/ambigs.cpp +++ b/ccutil/ambigs.cpp @@ -19,7 +19,10 @@ /////////////////////////////////////////////////////////////////////// #include "ambigs.h" + +#include #include "helpers.h" +#include "universalambigs.h" #ifdef _WIN32 #ifndef __GNUC__ @@ -31,6 +34,11 @@ namespace tesseract { +// Maximum line size: +// 10 for sizes of ambigs, tabs, abmig type and newline +// UNICHAR_LEN * (MAX_AMBIG_SIZE + 1) for each part of the ambig +const int kMaxAmbigStringSize = UNICHAR_LEN * (MAX_AMBIG_SIZE + 1); + AmbigSpec::AmbigSpec() { wrong_ngram[0] = INVALID_UNICHAR_ID; correct_fragments[0] = INVALID_UNICHAR_ID; @@ -41,14 +49,10 @@ AmbigSpec::AmbigSpec() { ELISTIZE(AmbigSpec); -void UnicharAmbigs::LoadUnicharAmbigs(FILE *AmbigFile, - inT64 end_offset, - int debug_level, - bool use_ambigs_for_adaption, - UNICHARSET *unicharset) { - int i, j; - UnicharIdVector *adaption_ambigs_entry; - for (i = 0; i < unicharset->size(); ++i) { +// Initializes the ambigs by adding a NULL pointer to each table. +void UnicharAmbigs::InitUnicharAmbigs(const UNICHARSET& unicharset, + bool use_ambigs_for_adaption) { + for (int i = 0; i < unicharset.size(); ++i) { replace_ambigs_.push_back(NULL); dang_ambigs_.push_back(NULL); one_to_one_definite_ambigs_.push_back(NULL); @@ -57,85 +61,103 @@ void UnicharAmbigs::LoadUnicharAmbigs(FILE *AmbigFile, reverse_ambigs_for_adaption_.push_back(NULL); } } +} + +// Loads the universal ambigs that are useful for any language. +void UnicharAmbigs::LoadUniversal(const UNICHARSET& encoder_set, + UNICHARSET* unicharset) { + FILE* fp = fmemopen(const_cast(kUniversalAmbigsFile), + ksizeofUniversalAmbigsFile, "rb"); + if (fp == NULL) return; + LoadUnicharAmbigs(encoder_set, fp, -1ll, 0, false, unicharset); + fclose(fp); +} + +void UnicharAmbigs::LoadUnicharAmbigs(const UNICHARSET& encoder_set, + FILE *ambig_file, + inT64 end_offset, + int debug_level, + bool use_ambigs_for_adaption, + UNICHARSET *unicharset) { + int i, j; + UnicharIdVector *adaption_ambigs_entry; if (debug_level) tprintf("Reading ambiguities\n"); - int TestAmbigPartSize; - int ReplacementAmbigPartSize; - // Maximum line size: - // 10 for sizes of ambigs, tabs, abmig type and newline - // UNICHAR_LEN * (MAX_AMBIG_SIZE + 1) for each part of the ambig + int test_ambig_part_size; + int replacement_ambig_part_size; // The space for buffer is allocated on the heap to avoid // GCC frame size warning. - const int kMaxAmbigStringSize = UNICHAR_LEN * (MAX_AMBIG_SIZE + 1); const int kBufferSize = 10 + 2 * kMaxAmbigStringSize; char *buffer = new char[kBufferSize]; - char ReplacementString[kMaxAmbigStringSize]; - UNICHAR_ID TestUnicharIds[MAX_AMBIG_SIZE + 1]; + char replacement_string[kMaxAmbigStringSize]; + UNICHAR_ID test_unichar_ids[MAX_AMBIG_SIZE + 1]; int line_num = 0; int type = NOT_AMBIG; // Determine the version of the ambigs file. int version = 0; - ASSERT_HOST(fgets(buffer, kBufferSize, AmbigFile) != NULL && + ASSERT_HOST(fgets(buffer, kBufferSize, ambig_file) != NULL && strlen(buffer) > 0); if (*buffer == 'v') { version = static_cast(strtol(buffer+1, NULL, 10)); ++line_num; } else { - rewind(AmbigFile); + rewind(ambig_file); } - while ((end_offset < 0 || ftell(AmbigFile) < end_offset) && - fgets(buffer, kBufferSize, AmbigFile) != NULL) { + while ((end_offset < 0 || ftell(ambig_file) < end_offset) && + fgets(buffer, kBufferSize, ambig_file) != NULL) { chomp_string(buffer); if (debug_level > 2) tprintf("read line %s\n", buffer); ++line_num; - if (!ParseAmbiguityLine(line_num, version, debug_level, *unicharset, - buffer, &TestAmbigPartSize, TestUnicharIds, - &ReplacementAmbigPartSize, - ReplacementString, &type)) continue; + if (!ParseAmbiguityLine(line_num, version, debug_level, encoder_set, + buffer, &test_ambig_part_size, test_unichar_ids, + &replacement_ambig_part_size, + replacement_string, &type)) continue; // Construct AmbigSpec and add it to the appropriate AmbigSpec_LIST. AmbigSpec *ambig_spec = new AmbigSpec(); - InsertIntoTable((type == REPLACE_AMBIG) ? replace_ambigs_ : dang_ambigs_, - TestAmbigPartSize, TestUnicharIds, - ReplacementAmbigPartSize, ReplacementString, type, - ambig_spec, unicharset); + if (!InsertIntoTable((type == REPLACE_AMBIG) ? replace_ambigs_ + : dang_ambigs_, + test_ambig_part_size, test_unichar_ids, + replacement_ambig_part_size, replacement_string, type, + ambig_spec, unicharset)) + continue; // Update one_to_one_definite_ambigs_. - if (TestAmbigPartSize == 1 && - ReplacementAmbigPartSize == 1 && type == DEFINITE_AMBIG) { - if (one_to_one_definite_ambigs_[TestUnicharIds[0]] == NULL) { - one_to_one_definite_ambigs_[TestUnicharIds[0]] = new UnicharIdVector(); + if (test_ambig_part_size == 1 && + replacement_ambig_part_size == 1 && type == DEFINITE_AMBIG) { + if (one_to_one_definite_ambigs_[test_unichar_ids[0]] == NULL) { + one_to_one_definite_ambigs_[test_unichar_ids[0]] = new UnicharIdVector(); } - one_to_one_definite_ambigs_[TestUnicharIds[0]]->push_back( + one_to_one_definite_ambigs_[test_unichar_ids[0]]->push_back( ambig_spec->correct_ngram_id); } // Update ambigs_for_adaption_. if (use_ambigs_for_adaption) { - for (i = 0; i < TestAmbigPartSize; ++i) { - if (ambigs_for_adaption_[TestUnicharIds[i]] == NULL) { - ambigs_for_adaption_[TestUnicharIds[i]] = new UnicharIdVector(); - } - adaption_ambigs_entry = ambigs_for_adaption_[TestUnicharIds[i]]; - const char *tmp_ptr = ReplacementString; - const char *tmp_ptr_end = ReplacementString + strlen(ReplacementString); - int step = unicharset->step(tmp_ptr); - while (step > 0) { - UNICHAR_ID id_to_insert = unicharset->unichar_to_id(tmp_ptr, step); - ASSERT_HOST(id_to_insert != INVALID_UNICHAR_ID); - // Add the new unichar id to adaption_ambigs_entry (only if the - // vector does not already contain it) keeping it in sorted order. - for (j = 0; j < adaption_ambigs_entry->size() && - (*adaption_ambigs_entry)[j] > id_to_insert; ++j); - if (j < adaption_ambigs_entry->size()) { - if ((*adaption_ambigs_entry)[j] != id_to_insert) { - adaption_ambigs_entry->insert(id_to_insert, j); + GenericVector encoding; + // Silently ignore invalid strings, as before, so it is safe to use a + // universal ambigs file. + if (unicharset->encode_string(replacement_string, true, &encoding, + NULL, NULL)) { + for (i = 0; i < test_ambig_part_size; ++i) { + if (ambigs_for_adaption_[test_unichar_ids[i]] == NULL) { + ambigs_for_adaption_[test_unichar_ids[i]] = new UnicharIdVector(); + } + adaption_ambigs_entry = ambigs_for_adaption_[test_unichar_ids[i]]; + for (int r = 0; r < encoding.size(); ++r) { + UNICHAR_ID id_to_insert = encoding[r]; + ASSERT_HOST(id_to_insert != INVALID_UNICHAR_ID); + // Add the new unichar id to adaption_ambigs_entry (only if the + // vector does not already contain it) keeping it in sorted order. + for (j = 0; j < adaption_ambigs_entry->size() && + (*adaption_ambigs_entry)[j] > id_to_insert; ++j); + if (j < adaption_ambigs_entry->size()) { + if ((*adaption_ambigs_entry)[j] != id_to_insert) { + adaption_ambigs_entry->insert(id_to_insert, j); + } + } else { + adaption_ambigs_entry->push_back(id_to_insert); } - } else { - adaption_ambigs_entry->push_back(id_to_insert); } - // Update tmp_ptr and step. - tmp_ptr += step; - step = tmp_ptr < tmp_ptr_end ? unicharset->step(tmp_ptr) : 0; } } } @@ -204,51 +226,96 @@ void UnicharAmbigs::LoadUnicharAmbigs(FILE *AmbigFile, bool UnicharAmbigs::ParseAmbiguityLine( int line_num, int version, int debug_level, const UNICHARSET &unicharset, - char *buffer, int *TestAmbigPartSize, UNICHAR_ID *TestUnicharIds, - int *ReplacementAmbigPartSize, char *ReplacementString, int *type) { + char *buffer, int *test_ambig_part_size, UNICHAR_ID *test_unichar_ids, + int *replacement_ambig_part_size, char *replacement_string, int *type) { + if (version > 1) { + // Simpler format is just wrong-string correct-string type\n. + STRING input(buffer); + GenericVector fields; + input.split(' ', &fields); + if (fields.size() != 3) { + if (debug_level) tprintf(kIllegalMsg, line_num); + return false; + } + // Encode wrong-string. + GenericVector unichars; + if (!unicharset.encode_string(fields[0].string(), true, &unichars, NULL, + NULL)) { + return false; + } + *test_ambig_part_size = unichars.size(); + if (*test_ambig_part_size > MAX_AMBIG_SIZE) { + if (debug_level) + tprintf("Too many unichars in ambiguity on line %d\n", line_num); + return false; + } + // Copy encoded string to output. + for (int i = 0; i < unichars.size(); ++i) + test_unichar_ids[i] = unichars[i]; + test_unichar_ids[unichars.size()] = INVALID_UNICHAR_ID; + // Encode replacement-string to check validity. + if (!unicharset.encode_string(fields[1].string(), true, &unichars, NULL, + NULL)) { + return false; + } + *replacement_ambig_part_size = unichars.size(); + if (*replacement_ambig_part_size > MAX_AMBIG_SIZE) { + if (debug_level) + tprintf("Too many unichars in ambiguity on line %d\n", line_num); + return false; + } + if (sscanf(fields[2].string(), "%d", type) != 1) { + if (debug_level) tprintf(kIllegalMsg, line_num); + return false; + } + snprintf(replacement_string, kMaxAmbigStringSize, "%s", fields[1].string()); + return true; + } int i; char *token; char *next_token; if (!(token = strtok_r(buffer, kAmbigDelimiters, &next_token)) || - !sscanf(token, "%d", TestAmbigPartSize) || TestAmbigPartSize <= 0) { + !sscanf(token, "%d", test_ambig_part_size) || test_ambig_part_size <= 0) { if (debug_level) tprintf(kIllegalMsg, line_num); return false; } - if (*TestAmbigPartSize > MAX_AMBIG_SIZE) { - tprintf("Too many unichars in ambiguity on line %d\n"); + if (*test_ambig_part_size > MAX_AMBIG_SIZE) { + if (debug_level) + tprintf("Too many unichars in ambiguity on line %d\n", line_num); return false; } - for (i = 0; i < *TestAmbigPartSize; ++i) { + for (i = 0; i < *test_ambig_part_size; ++i) { if (!(token = strtok_r(NULL, kAmbigDelimiters, &next_token))) break; if (!unicharset.contains_unichar(token)) { if (debug_level) tprintf(kIllegalUnicharMsg, token); break; } - TestUnicharIds[i] = unicharset.unichar_to_id(token); + test_unichar_ids[i] = unicharset.unichar_to_id(token); } - TestUnicharIds[i] = INVALID_UNICHAR_ID; + test_unichar_ids[i] = INVALID_UNICHAR_ID; - if (i != *TestAmbigPartSize || + if (i != *test_ambig_part_size || !(token = strtok_r(NULL, kAmbigDelimiters, &next_token)) || - !sscanf(token, "%d", ReplacementAmbigPartSize) || - *ReplacementAmbigPartSize <= 0) { + !sscanf(token, "%d", replacement_ambig_part_size) || + *replacement_ambig_part_size <= 0) { if (debug_level) tprintf(kIllegalMsg, line_num); return false; } - if (*ReplacementAmbigPartSize > MAX_AMBIG_SIZE) { - tprintf("Too many unichars in ambiguity on line %d\n"); + if (*replacement_ambig_part_size > MAX_AMBIG_SIZE) { + if (debug_level) + tprintf("Too many unichars in ambiguity on line %d\n", line_num); return false; } - ReplacementString[0] = '\0'; - for (i = 0; i < *ReplacementAmbigPartSize; ++i) { + replacement_string[0] = '\0'; + for (i = 0; i < *replacement_ambig_part_size; ++i) { if (!(token = strtok_r(NULL, kAmbigDelimiters, &next_token))) break; - strcat(ReplacementString, token); + strcat(replacement_string, token); if (!unicharset.contains_unichar(token)) { if (debug_level) tprintf(kIllegalUnicharMsg, token); break; } } - if (i != *ReplacementAmbigPartSize) { + if (i != *replacement_ambig_part_size) { if (debug_level) tprintf(kIllegalMsg, line_num); return false; } @@ -271,20 +338,20 @@ bool UnicharAmbigs::ParseAmbiguityLine( return true; } -void UnicharAmbigs::InsertIntoTable( - UnicharAmbigsVector &table, int TestAmbigPartSize, - UNICHAR_ID *TestUnicharIds, int ReplacementAmbigPartSize, - const char *ReplacementString, int type, +bool UnicharAmbigs::InsertIntoTable( + UnicharAmbigsVector &table, int test_ambig_part_size, + UNICHAR_ID *test_unichar_ids, int replacement_ambig_part_size, + const char *replacement_string, int type, AmbigSpec *ambig_spec, UNICHARSET *unicharset) { ambig_spec->type = static_cast(type); - if (TestAmbigPartSize == 1 && ReplacementAmbigPartSize == 1 && - unicharset->to_lower(TestUnicharIds[0]) == - unicharset->to_lower(unicharset->unichar_to_id(ReplacementString))) { + if (test_ambig_part_size == 1 && replacement_ambig_part_size == 1 && + unicharset->to_lower(test_unichar_ids[0]) == + unicharset->to_lower(unicharset->unichar_to_id(replacement_string))) { ambig_spec->type = CASE_AMBIG; } ambig_spec->wrong_ngram_size = - UnicharIdArrayUtils::copy(TestUnicharIds, ambig_spec->wrong_ngram); + UnicharIdArrayUtils::copy(test_unichar_ids, ambig_spec->wrong_ngram); // Since we need to maintain a constant number of unichar positions in // order to construct ambig_blob_choices vector in NoDangerousAmbig(), for @@ -297,21 +364,21 @@ void UnicharAmbigs::InsertIntoTable( // Insert the corresponding correct ngram into the unicharset. // Unicharset code assumes that the "base" ngram is inserted into // the unicharset before fragments of this ngram are inserted. - unicharset->unichar_insert(ReplacementString); + unicharset->unichar_insert(replacement_string); ambig_spec->correct_ngram_id = - unicharset->unichar_to_id(ReplacementString); - if (ReplacementAmbigPartSize > 1) { + unicharset->unichar_to_id(replacement_string); + if (replacement_ambig_part_size > 1) { unicharset->set_isngram(ambig_spec->correct_ngram_id, true); } // Add the corresponding fragments of the wrong ngram to unicharset. int i; - for (i = 0; i < TestAmbigPartSize; ++i) { + for (i = 0; i < test_ambig_part_size; ++i) { UNICHAR_ID unichar_id; - if (TestAmbigPartSize == 1) { + if (test_ambig_part_size == 1) { unichar_id = ambig_spec->correct_ngram_id; } else { STRING frag_str = CHAR_FRAGMENT::to_string( - ReplacementString, i, TestAmbigPartSize, false); + replacement_string, i, test_ambig_part_size, false); unicharset->unichar_insert(frag_str.string()); unichar_id = unicharset->unichar_to_id(frag_str.string()); } @@ -321,11 +388,14 @@ void UnicharAmbigs::InsertIntoTable( // Add AmbigSpec for this ambiguity to the corresponding AmbigSpec_LIST. // Keep AmbigSpec_LISTs sorted by AmbigSpec.wrong_ngram. - if (table[TestUnicharIds[0]] == NULL) { - table[TestUnicharIds[0]] = new AmbigSpec_LIST(); + if (table[test_unichar_ids[0]] == NULL) { + table[test_unichar_ids[0]] = new AmbigSpec_LIST(); } - table[TestUnicharIds[0]]->add_sorted( - AmbigSpec::compare_ambig_specs, false, ambig_spec); + if (table[test_unichar_ids[0]]->add_sorted( + AmbigSpec::compare_ambig_specs, true, ambig_spec)) + return true; + delete ambig_spec; + return false; } } // namespace tesseract diff --git a/ccutil/ambigs.h b/ccutil/ambigs.h index a72e751633..549b2ac468 100644 --- a/ccutil/ambigs.h +++ b/ccutil/ambigs.h @@ -123,7 +123,10 @@ class AmbigSpec : public ELIST_LINK { *reinterpret_cast(spec1); const AmbigSpec *s2 = *reinterpret_cast(spec2); - return UnicharIdArrayUtils::compare(s1->wrong_ngram, s2->wrong_ngram); + int result = UnicharIdArrayUtils::compare(s1->wrong_ngram, s2->wrong_ngram); + if (result != 0) return result; + return UnicharIdArrayUtils::compare(s1->correct_fragments, + s2->correct_fragments); } UNICHAR_ID wrong_ngram[MAX_AMBIG_SIZE + 1]; @@ -150,6 +153,13 @@ class UnicharAmbigs { const UnicharAmbigsVector &dang_ambigs() const { return dang_ambigs_; } const UnicharAmbigsVector &replace_ambigs() const { return replace_ambigs_; } + // Initializes the ambigs by adding a NULL pointer to each table. + void InitUnicharAmbigs(const UNICHARSET& unicharset, + bool use_ambigs_for_adaption); + + // Loads the universal ambigs that are useful for any language. + void LoadUniversal(const UNICHARSET& encoder_set, UNICHARSET* unicharset); + // Fills in two ambiguity tables (replaceable and dangerous) with information // read from the ambigs file. An ambiguity table is an array of lists. // The array is indexed by a class id. Each entry in the table provides @@ -160,7 +170,10 @@ class UnicharAmbigs { // one_to_one_definite_ambigs_. This vector is also indexed by the class id // of the wrong part of the ambiguity and each entry contains a vector of // unichar ids that are ambiguous to it. - void LoadUnicharAmbigs(FILE *ambigs_file, inT64 end_offset, int debug_level, + // encoder_set is used to encode the ambiguity strings, undisturbed by new + // unichar_ids that may be created by adding the ambigs. + void LoadUnicharAmbigs(const UNICHARSET& encoder_set, + FILE *ambigs_file, inT64 end_offset, int debug_level, bool use_ambigs_for_adaption, UNICHARSET *unicharset); // Returns definite 1-1 ambigs for the given unichar id. @@ -191,17 +204,18 @@ class UnicharAmbigs { } private: - bool ParseAmbiguityLine(int line_num, int version, int debug_level, const UNICHARSET &unicharset, char *buffer, - int *TestAmbigPartSize, UNICHAR_ID *TestUnicharIds, - int *ReplacementAmbigPartSize, - char *ReplacementString, int *type); - void InsertIntoTable(UnicharAmbigsVector &table, - int TestAmbigPartSize, UNICHAR_ID *TestUnicharIds, - int ReplacementAmbigPartSize, - const char *ReplacementString, int type, + int *test_ambig_part_size, + UNICHAR_ID *test_unichar_ids, + int *replacement_ambig_part_size, + char *replacement_string, int *type); + bool InsertIntoTable(UnicharAmbigsVector &table, + int test_ambig_part_size, UNICHAR_ID *test_unichar_ids, + int replacement_ambig_part_size, + const char *replacement_string, int type, AmbigSpec *ambig_spec, UNICHARSET *unicharset); + UnicharAmbigsVector dang_ambigs_; UnicharAmbigsVector replace_ambigs_; GenericVector one_to_one_definite_ambigs_; diff --git a/ccutil/doubleptr.h b/ccutil/doubleptr.h new file mode 100644 index 0000000000..4cef16b59d --- /dev/null +++ b/ccutil/doubleptr.h @@ -0,0 +1,93 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// Author: rays@google.com (Ray Smith) +/////////////////////////////////////////////////////////////////////// +// File: doubleptr.h +// Description: Double-ended pointer that keeps pointing correctly even +// when reallocated or copied. +// Author: Ray Smith +// Created: Wed Mar 14 12:22:57 PDT 2012 +// +// (C) Copyright 2012, Google Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +/////////////////////////////////////////////////////////////////////// + +#ifndef TESSERACT_CCUTIL_DOUBLEPTR_H_ +#define TESSERACT_CCUTIL_DOUBLEPTR_H_ + +#include "errcode.h" + +namespace tesseract { + +// A smart pointer class that implements a double-ended pointer. Each end +// points to the other end. The copy constructor and operator= have MOVE +// semantics, meaning that the relationship with the other end moves to the +// destination of the copy, leaving the source unattached. +// For this reason both the copy constructor and the operator= take a non-const +// reference argument, and the const reference versions cannot be used. +// DoublePtr is useful to incorporate into structures that are part of a +// collection such as GenericVector or STL containers, where reallocs can +// relocate the members. DoublePtr is also useful in a GenericHeap, where it +// can correctly maintain the pointer to an element of the heap despite it +// getting moved around on the heap. +class DoublePtr { + public: + DoublePtr() : other_end_(NULL) {} + // Copy constructor steals the partner off src and is therefore a non + // const reference arg. + // Copying a const DoublePtr generates a compiler error. + DoublePtr(DoublePtr& src) { + other_end_ = src.other_end_; + if (other_end_ != NULL) { + other_end_->other_end_ = this; + src.other_end_ = NULL; + } + } + // Operator= steals the partner off src, and therefore needs src to be a non- + // const reference. + // Assigning from a const DoublePtr generates a compiler error. + void operator=(DoublePtr& src) { + Disconnect(); + other_end_ = src.other_end_; + if (other_end_ != NULL) { + other_end_->other_end_ = this; + src.other_end_ = NULL; + } + } + + // Connects this and other, discarding any existing connections. + void Connect(DoublePtr* other) { + other->Disconnect(); + Disconnect(); + other->other_end_ = this; + other_end_ = other; + } + // Disconnects this and other, making OtherEnd() return NULL for both. + void Disconnect() { + if (other_end_ != NULL) { + other_end_->other_end_ = NULL; + other_end_ = NULL; + } + } + // Returns the pointer to the other end of the double pointer. + DoublePtr* OtherEnd() const { + return other_end_; + } + + private: + // Pointer to the other end of the link. It is always true that either + // other_end_ == NULL or other_end_->other_end_ == this. + DoublePtr* other_end_; +}; + +} // namespace tesseract. + +#endif // THIRD_PARTY_TESSERACT_CCUTIL_DOUBLEPTR_H_ diff --git a/ccutil/errcode.h b/ccutil/errcode.h index 2bc2a03c9c..eaa0b55d98 100644 --- a/ccutil/errcode.h +++ b/ccutil/errcode.h @@ -90,12 +90,6 @@ const ERRCODE ASSERT_FAILED = "Assert failed"; void signal_exit( // int signal_code //Signal which ); -extern "C" -{ - void err_exit(); - //The real signal - void signal_termination_handler(int sig); -}; void set_global_loc_code(int loc_code); diff --git a/ccutil/genericheap.h b/ccutil/genericheap.h new file mode 100644 index 0000000000..512decf34c --- /dev/null +++ b/ccutil/genericheap.h @@ -0,0 +1,225 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// Author: rays@google.com (Ray Smith) +/////////////////////////////////////////////////////////////////////// +// File: genericheap.h +// Description: Template heap class. +// Author: Ray Smith, based on Dan Johnson's original code. +// Created: Wed Mar 14 08:13:00 PDT 2012 +// +// (C) Copyright 2012, Google Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +/////////////////////////////////////////////////////////////////////// + +#include "errcode.h" +#include "genericvector.h" + +#ifndef TESSERACT_CCUTIL_GENERICHEAP_H_ +#define TESSERACT_CCUTIL_GENERICHEAP_H_ + +namespace tesseract { + +// GenericHeap requires 1 template argument: +// Pair will normally be either KDPairInc or KDPairDec +// for some arbitrary Key and scalar, smart pointer, or non-ownership pointer +// Data type, according to whether a MIN heap or a MAX heap is desired, +// respectively. Using KDPtrPairInc or KDPtrPairDec, +// GenericHeap can also handle simple Data pointers and own them. +// If no additional data is required, Pair can also be a scalar, since +// GenericHeap doesn't look inside it except for operator<. +// +// The heap is stored as a packed binary tree in an array hosted by a +// GenericVector, with the invariant that the children of each node are +// both NOT Pair::operator< the parent node. KDPairInc defines Pair::operator< +// to use Key::operator< to generate a MIN heap and KDPairDec defines +// Pair::operator< to use Key::operator> to generate a MAX heap by reversing +// all the comparisons. +// See http://en.wikipedia.org/wiki/Heap_(data_structure) for more detail on +// the basic heap implementation. +// +// Insertion and removal are both O(log n) and, unlike the STL heap, an +// explicit Reshuffle function allows a node to be repositioned in time O(log n) +// after changing its value. +// +// Accessing the element for revaluation is a more complex matter, since the +// index and pointer can be changed arbitrarily by heap operations. +// Revaluation can be done by making the Data type in the Pair derived from or +// contain a DoublePtr as its first data element, making it possible to convert +// the pointer to a Pair using KDPairInc::RecastDataPointer. +template +class GenericHeap { + public: + GenericHeap() {} + // The initial size is only a GenericVector::reserve. It is not enforced as + // the size limit of the heap. Caller must implement their own enforcement. + explicit GenericHeap(int initial_size) { + heap_.reserve(initial_size); + } + + // Simple accessors. + bool empty() const { + return heap_.empty(); + } + int size() const { + return heap_.size(); + } + int size_reserved() const { + return heap_.size_reserved(); + } + void clear() { + // Clear truncates to 0 to keep the number reserved in tact. + heap_.truncate(0); + } + // Provides access to the underlying vector. + // Caution! any changes that modify the keys will invalidate the heap! + GenericVector* heap() { + return &heap_; + } + + // Add entry to the heap, keeping the smallest item at the top, by operator<. + // Note that *entry is used as the source of operator=, but it is non-const + // to allow for a smart pointer to be contained within. + // Time = O(log n). + void Push(Pair* entry) { + int hole_index = heap_.size(); + // Make a hole in the end of heap_ and sift it up to be the correct + // location for the new *entry. To avoid needing a default constructor + // for primitive types, and to allow for use of DoublePtr in the Pair + // somewhere, we have to incur a double copy here. + heap_.push_back(*entry); + *entry = heap_.back(); + hole_index = SiftUp(hole_index, *entry); + heap_[hole_index] = *entry; + } + + // Get the value of the top (smallest, defined by operator< ) element. + const Pair& PeekTop() const { + return heap_[0]; + } + + // Removes the top element of the heap. If entry is not NULL, the element + // is copied into *entry, otherwise it is discarded. + // Returns false if the heap was already empty. + // Time = O(log n). + bool Pop(Pair* entry) { + int new_size = heap_.size() - 1; + if (new_size < 0) + return false; // Already empty. + if (entry != NULL) + *entry = heap_[0]; + if (new_size > 0) { + // Sift the hole at the start of the heap_ downwards to match the last + // element. + Pair hole_pair = heap_[new_size]; + heap_.truncate(new_size); + int hole_index = SiftDown(0, hole_pair); + heap_[hole_index] = hole_pair; + } else { + heap_.truncate(new_size); + } + return true; + } + + // Removes the MAXIMUM element of the heap. (MIN from a MAX heap.) If entry is + // not NULL, the element is copied into *entry, otherwise it is discarded. + // Time = O(n). Returns false if the heap was already empty. + bool PopWorst(Pair* entry) { + int heap_size = heap_.size(); + if (heap_size == 0) return false; // It cannot be empty! + + // Find the maximum element. Its index is guaranteed to be greater than + // the index of the parent of the last element, since by the heap invariant + // the parent must be less than or equal to the children. + int worst_index = heap_size - 1; + int end_parent = ParentNode(worst_index); + for (int i = worst_index - 1; i > end_parent; --i) { + if (heap_[worst_index] < heap_[i]) + worst_index = i; + } + // Extract the worst element from the heap, leaving a hole at worst_index. + if (entry != NULL) + *entry = heap_[worst_index]; + --heap_size; + if (heap_size > 0) { + // Sift the hole upwards to match the last element of the heap_ + Pair hole_pair = heap_[heap_size]; + int hole_index = SiftUp(worst_index, hole_pair); + heap_[hole_index] = hole_pair; + } + heap_.truncate(heap_size); + return true; + } + + // The pointed-to Pair has changed its key value, so the location of pair + // is reshuffled to maintain the heap invariant. + // Must be a valid pointer to an element of the heap_! + // Caution! Since GenericHeap is based on GenericVector, reallocs may occur + // whenever the vector is extended and elements may get shuffled by any + // Push or Pop operation. Therefore use this function only if Data in Pair is + // of type DoublePtr, derived (first) from DoublePtr, or has a DoublePtr as + // its first element. Reshuffles the heap to maintain the invariant. + // Time = O(log n). + void Reshuffle(Pair* pair) { + int index = pair - &heap_[0]; + Pair hole_pair = heap_[index]; + index = SiftDown(index, hole_pair); + index = SiftUp(index, hole_pair); + heap_[index] = hole_pair; + } + + private: + // A hole in the heap exists at hole_index, and we want to fill it with the + // given pair. SiftUp sifts the hole upward to the correct position and + // returns the destination index without actually putting pair there. + int SiftUp(int hole_index, const Pair& pair) { + int parent; + while (hole_index > 0 && pair < heap_[parent = ParentNode(hole_index)]) { + heap_[hole_index] = heap_[parent]; + hole_index = parent; + } + return hole_index; + } + + // A hole in the heap exists at hole_index, and we want to fill it with the + // given pair. SiftDown sifts the hole downward to the correct position and + // returns the destination index without actually putting pair there. + int SiftDown(int hole_index, const Pair& pair) { + int heap_size = heap_.size(); + int child; + while ((child = LeftChild(hole_index)) < heap_size) { + if (child + 1 < heap_size && heap_[child + 1] < heap_[child]) + ++child; + if (heap_[child] < pair) { + heap_[hole_index] = heap_[child]; + hole_index = child; + } else { + break; + } + } + return hole_index; + } + + // Functions to navigate the tree. Unlike the original implementation, we + // store the root at index 0. + int ParentNode(int index) const { + return (index + 1) / 2 - 1; + } + int LeftChild(int index) const { + return index * 2 + 1; + } + + private: + GenericVector heap_; +}; + +} // namespace tesseract + +#endif // TESSERACT_CCUTIL_GENERICHEAP_H_ diff --git a/ccutil/genericvector.h b/ccutil/genericvector.h index 5ac3532a4f..c1b2f059b1 100644 --- a/ccutil/genericvector.h +++ b/ccutil/genericvector.h @@ -20,6 +20,7 @@ #ifndef TESSERACT_CCUTIL_GENERICVECTOR_H_ #define TESSERACT_CCUTIL_GENERICVECTOR_H_ +#include #include #include @@ -34,8 +35,13 @@ template class GenericVector { public: - GenericVector() { this->init(kDefaultVectorSize); } - explicit GenericVector(int size) { this->init(size); } + GenericVector() { + init(kDefaultVectorSize); + } + GenericVector(int size, T init_val) { + init(size); + init_to_size(size, init_val); + } // Copy GenericVector(const GenericVector& other) { @@ -45,7 +51,7 @@ class GenericVector { GenericVector &operator+=(const GenericVector& other); GenericVector &operator=(const GenericVector& other); - virtual ~GenericVector(); + ~GenericVector(); // Reserve some memory. void reserve(int size); @@ -59,6 +65,9 @@ class GenericVector { int size() const { return size_used_; } + int size_reserved() const { + return size_reserved_; + } int length() const { return size_used_; @@ -73,6 +82,8 @@ class GenericVector { T &get(int index) const; T &back() const; T &operator[](int index) const; + // Returns the last object and removes it. + T pop_back(); // Return the index of the T object. // This method NEEDS a compare_callback to be passed to @@ -105,11 +116,11 @@ class GenericVector { // Removes an element at the given index and // shifts the remaining elements to the left. - virtual void remove(int index); + void remove(int index); // Truncates the array to the given size by removing the end. // If the current size is less, the array is not expanded. - virtual void truncate(int size) { + void truncate(int size) { if (size < size_used_) size_used_ = size; } @@ -126,7 +137,7 @@ class GenericVector { // All the owned callbacks are also deleted. // If you don't want the callbacks to be deleted, before calling clear, set // the callback to NULL. - virtual void clear(); + void clear(); // Delete objects pointed to by data_[i] void delete_data_pointers(); @@ -147,12 +158,12 @@ class GenericVector { bool read(FILE* f, TessResultCallback3* cb, bool swap); // Writes a vector of simple types to the given file. Assumes that bitwise // read/write of T will work. Returns false in case of error. - virtual bool Serialize(FILE* fp) const; + bool Serialize(FILE* fp) const; // Reads a vector of simple types from the given file. Assumes that bitwise // read/write will work with ReverseN according to sizeof(T). // Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. - virtual bool DeSerialize(bool swap, FILE* fp); + bool DeSerialize(bool swap, FILE* fp); // Writes a vector of classes to the given file. Assumes the existence of // bool T::Serialize(FILE* fp) const that returns false in case of error. // Returns false in case of error. @@ -262,7 +273,32 @@ class GenericVector { return result; } + // Returns the index of what would be the target_index_th item in the array + // if the members were sorted, without actually sorting. Members are + // shuffled around, but it takes O(n) time. + // NOTE: uses operator< and operator== on the members. + int choose_nth_item(int target_index) { + // Make sure target_index is legal. + if (target_index < 0) + target_index = 0; // ensure legal + else if (target_index >= size_used_) + target_index = size_used_ - 1; + unsigned int seed = 1; + return choose_nth_item(target_index, 0, size_used_, &seed); + } + + // Swaps the elements with the given indices. + void swap(int index1, int index2) { + if (index1 != index2) { + T tmp = data_[index1]; + data_[index1] = data_[index2]; + data_[index2] = tmp; + } + } + protected: + // Internal recursive version of choose_nth_item. + int choose_nth_item(int target_index, int start, int end, unsigned int* seed); // Init the object, allocating size memory. void init(int size); @@ -328,7 +364,7 @@ class PointerVector : public GenericVector { public: PointerVector() : GenericVector() { } explicit PointerVector(int size) : GenericVector(size) { } - virtual ~PointerVector() { + ~PointerVector() { // Clear must be called here, even though it is called again by the base, // as the base will call the wrong clear. clear(); @@ -355,14 +391,14 @@ class PointerVector : public GenericVector { // Removes an element at the given index and // shifts the remaining elements to the left. - virtual void remove(int index) { + void remove(int index) { delete GenericVector::data_[index]; GenericVector::remove(index); } // Truncates the array to the given size by removing the end. // If the current size is less, the array is not expanded. - virtual void truncate(int size) { + void truncate(int size) { for (int i = size; i < GenericVector::size_used_; ++i) delete GenericVector::data_[i]; GenericVector::truncate(size); @@ -394,14 +430,14 @@ class PointerVector : public GenericVector { // All the owned callbacks are also deleted. // If you don't want the callbacks to be deleted, before calling clear, set // the callback to NULL. - virtual void clear() { + void clear() { GenericVector::delete_data_pointers(); GenericVector::clear(); } // Writes a vector of simple types to the given file. Assumes that bitwise // read/write of T will work. Returns false in case of error. - virtual bool Serialize(FILE* fp) const { + bool Serialize(FILE* fp) const { inT32 used = GenericVector::size_used_; if (fwrite(&used, sizeof(used), 1, fp) != 1) return false; for (int i = 0; i < used; ++i) { @@ -416,7 +452,7 @@ class PointerVector : public GenericVector { // Also needs T::T(), as new T is used in this function. // Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. - virtual bool DeSerialize(bool swap, FILE* fp) { + bool DeSerialize(bool swap, FILE* fp) { inT32 reserved; if (fread(&reserved, sizeof(reserved), 1, fp) != 1) return false; if (swap) Reverse32(&reserved); @@ -515,7 +551,8 @@ T &GenericVector::get(int index) const { template T &GenericVector::operator[](int index) const { - return data_[index]; + assert(index >= 0 && index < size_used_); + return data_[index]; } template @@ -523,6 +560,12 @@ T &GenericVector::back() const { ASSERT_HOST(size_used_ > 0); return data_[size_used_ - 1]; } +// Returns the last object and removes it. +template +T GenericVector::pop_back() { + ASSERT_HOST(size_used_ > 0); + return data_[--size_used_]; +} // Return the object from an index. template @@ -536,7 +579,7 @@ void GenericVector::set(T t, int index) { // at the specified index. template void GenericVector::insert(T t, int index) { - ASSERT_HOST(index >= 0 && index < size_used_); + ASSERT_HOST(index >= 0 && index <= size_used_); if (size_reserved_ == size_used_) double_the_size(); for (int i = size_used_; i > index; --i) { @@ -642,7 +685,8 @@ void GenericVector::set_clear_callback(TessCallback1* cb) { // Add a callback to be called to delete the elements when the array took // their ownership. template -void GenericVector::set_compare_callback(TessResultCallback2* cb) { +void GenericVector::set_compare_callback( + TessResultCallback2* cb) { compare_cb_ = cb; } @@ -804,4 +848,61 @@ void GenericVector::sort() { sort(&tesseract::sort_cmp); } +// Internal recursive version of choose_nth_item. +// The algorithm used comes from "Algorithms" by Sedgewick: +// http://books.google.com/books/about/Algorithms.html?id=idUdqdDXqnAC +// The principle is to choose a random pivot, and move everything less than +// the pivot to its left, and everything greater than the pivot to the end +// of the array, then recurse on the part that contains the desired index, or +// just return the answer if it is in the equal section in the middle. +// The random pivot guarantees average linear time for the same reason that +// n times vector::push_back takes linear time on average. +// target_index, start and and end are all indices into the full array. +// Seed is a seed for rand_r for thread safety purposes. Its value is +// unimportant as the random numbers do not affect the result except +// between equal answers. +template +int GenericVector::choose_nth_item(int target_index, int start, int end, + unsigned int* seed) { + // Number of elements to process. + int num_elements = end - start; + // Trivial cases. + if (num_elements <= 1) + return start; + if (num_elements == 2) { + if (data_[start] < data_[start + 1]) { + return target_index > start ? start + 1 : start; + } else { + return target_index > start ? start : start + 1; + } + } + // Place the pivot at start. + int pivot = rand_r(seed) % num_elements + start; + swap(pivot, start); + // The invariant condition here is that items [start, next_lesser) are less + // than the pivot (which is at index next_lesser) and items + // [prev_greater, end) are greater than the pivot, with items + // [next_lesser, prev_greater) being equal to the pivot. + int next_lesser = start; + int prev_greater = end; + for (int next_sample = start + 1; next_sample < prev_greater;) { + if (data_[next_sample] < data_[next_lesser]) { + swap(next_lesser++, next_sample++); + } else if (data_[next_sample] == data_[next_lesser]) { + ++next_sample; + } else { + swap(--prev_greater, next_sample); + } + } + // Now the invariant is set up, we recurse on just the section that contains + // the desired index. + if (target_index < next_lesser) + return choose_nth_item(target_index, start, next_lesser, seed); + else if (target_index < prev_greater) + return next_lesser; // In equal bracket. + else + return choose_nth_item(target_index, prev_greater, end, seed); +} + + #endif // TESSERACT_CCUTIL_GENERICVECTOR_H_ diff --git a/ccutil/globaloc.cpp b/ccutil/globaloc.cpp index 543e2e2386..6f6606dfc2 100644 --- a/ccutil/globaloc.cpp +++ b/ccutil/globaloc.cpp @@ -18,82 +18,54 @@ **********************************************************************/ #include +#ifdef __linux__ +#include // For SYS_gettid. +#include // For syscall itself. +#endif +#include "allheaders.h" #include "errcode.h" #include "tprintf.h" -/*inT16 global_loc_code = LOC_INIT;//location code -inT16 global_subloc_code = SUBLOC_NORM; - //pass2 subloc code -inT16 global_subsubloc_code = SUBSUBLOC_OTHER; - //location code -inT16 global_abort_code = NO_ABORT_CODE; - //Prog abort code -*/ -void signal_exit( // - int signal_code //Signal which - ) { - /*int exit_status; +// Size of thread-id array of pixes to keep in case of crash. +const int kMaxNumThreadPixes = 32768; - if ((global_loc_code == LOC_PASS2) || (global_loc_code == LOC_FUZZY_SPACE)) - global_loc_code += global_subloc_code + global_subsubloc_code; +Pix* global_crash_pixes[kMaxNumThreadPixes]; - if (signal_code < 0) { - exit_status = global_loc_code * 8 + global_abort_code * 2 + 1; - tprintf ("Signal_exit %d ABORT. LocCode: %d AbortCode: %d\n", - exit_status, global_loc_code, global_abort_code); +void SavePixForCrash(int resolution, Pix* pix) { +#ifdef __linux__ + int thread_id = syscall(SYS_gettid) % kMaxNumThreadPixes; + pixDestroy(&global_crash_pixes[thread_id]); + if (pix != NULL) { + Pix* clone = pixClone(pix); + pixSetXRes(clone, resolution); + pixSetYRes(clone, resolution); + global_crash_pixes[thread_id] = clone; } - else { - exit_status = global_loc_code * 8 + signal_code * 2; - tprintf ("Signal_exit %d SIGNAL ABORT. LocCode: %d SignalCode: %d\n", - exit_status, global_loc_code, signal_code); - } - - exit(exit_status);*/ - exit(signal_code); -} - - -/************************************************************************* - * err_exit() - * All program exits should go through this point. It allows a meaningful status - * code to be generated for the real exit() call. The status code is made up - * as follows: - * Bit 0 : 1 = Program Abort 0 = System Abort - * Bits 1,2 : IF bit 0 = 1 THEN ERRCODE::abort_code - * ELSE 0 = Bus Err or Seg Vi - * 1 = Floating point exception - * 2 = TimeOut (Signal 15 from command timer) - * 3 = Any other signal - * Bits 3..7 : Location code NEVER 0 ! - *************************************************************************/ - -//extern "C" { - -void err_exit() { - signal_exit (-1); +#endif } - -void signal_termination_handler(int sig) { - const ERRCODE SIGNAL_HANDLER_ERR = "Signal_termination_handler called"; - SIGNAL_HANDLER_ERR.error("signal_termination_handler", ABORT, "Code %d", sig); - switch (sig) { - case SIGABRT: - signal_exit (-1); //use abort code - // case SIGBUS: - case SIGSEGV: - signal_exit (0); - case SIGFPE: - signal_exit (1); //floating point - case SIGTERM: - signal_exit (2); //timeout by cmdtimer - default: - signal_exit (3); //Anything else +// CALL ONLY from a signal handler! Writes a crash image to stderr. +void signal_exit(int signal_code) { + tprintf("Received signal %d!\n", signal_code); +#ifdef __linux__ + int thread_id = syscall(SYS_gettid) % kMaxNumThreadPixes; + if (global_crash_pixes[thread_id] != NULL) { + fprintf(stderr, "Crash caused by image with resolution %d\n", + pixGetYRes(global_crash_pixes[thread_id])); + fprintf(stderr, "\n"); + pixWriteStreamPng(stderr, global_crash_pixes[thread_id], 0.0); + fprintf(stderr, "\n\n"); } + // Raise an uncaught signal, so as to get a useful stack trace. + raise(SIGILL); +#else + abort(); +#endif } - -//}; //end extern "C" +void err_exit() { + ASSERT_HOST("Fatal error encountered!" == NULL); +} void set_global_loc_code(int loc_code) { diff --git a/ccutil/globaloc.h b/ccutil/globaloc.h index b70154d84f..41438194c8 100644 --- a/ccutil/globaloc.h +++ b/ccutil/globaloc.h @@ -22,14 +22,14 @@ #include "host.h" -void signal_exit( // - int signal_code //Signal which - ); -//extern "C" { +// Saves a clone of the given pix, and notes its resolution in thread-specific +// data, so that the image can be written prior to a crash. +struct Pix; +void SavePixForCrash(int resolution, Pix* pix); + +void signal_exit(int signal_code); + void err_exit(); - //The real signal -void signal_termination_handler(int sig); -//}; void set_global_loc_code(int loc_code); diff --git a/ccutil/kdpair.h b/ccutil/kdpair.h new file mode 100644 index 0000000000..bb7743ed7c --- /dev/null +++ b/ccutil/kdpair.h @@ -0,0 +1,189 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// Author: rays@google.com (Ray Smith) +/////////////////////////////////////////////////////////////////////// +// File: kdpair.h +// Description: Template pair class like STL pair but geared towards +// the Key+Data design pattern in which some data needs +// to be sorted or kept in a heap sorted on some separate key. +// Author: Ray Smith. +// Created: Thu Mar 15 14:48:05 PDT 2012 +// +// (C) Copyright 2012, Google Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +/////////////////////////////////////////////////////////////////////// + +#ifndef TESSERACT_CCUTIL_KDPAIR_H_ +#define TESSERACT_CCUTIL_KDPAIR_H_ + +#include "genericvector.h" + +namespace tesseract { + +// A useful base struct to facilitate the common operation of sorting a vector +// of simple or smart-pointer data using a separate key. Similar to STL pair. +template +struct KDPair { + KDPair() {} + KDPair(Key k, Data d) : data(d), key(k) {} + + int operator==(const KDPair& other) const { + return key == other.key; + } + + // WARNING! Keep data as the first element! KDPairInc and KDPairDec depend + // on the order of these elements so they can downcast pointers appropriately + // for use by GenericHeap::Reshuffle. + Data data; + Key key; +}; +// Specialization of KDPair to provide operator< for sorting in increasing order +// and recasting of data pointers for use with DoublePtr. +template +struct KDPairInc : public KDPair { + KDPairInc() {} + KDPairInc(Key k, Data d) : KDPair(k, d) {} + // Operator< facilitates sorting in increasing order. + int operator<(const KDPairInc& other) const { + return this->key < other.key; + } + // Returns the input Data pointer recast to a KDPairInc pointer. + // Just casts a pointer to the first element to a pointer to the whole struct. + static KDPairInc* RecastDataPointer(Data* data_ptr) { + return reinterpret_cast(data_ptr); + } +}; +// Specialization of KDPair to provide operator< for sorting in decreasing order +// and recasting of data pointers for use with DoublePtr. +template +struct KDPairDec : public KDPair { + KDPairDec() {} + KDPairDec(Key k, Data d) : KDPair(k, d) {} + // Operator< facilitates sorting in decreasing order by using operator> on + // the key values. + int operator<(const KDPairDec& other) const { + return this->key > other.key; + } + // Returns the input Data pointer recast to a KDPairDec pointer. + // Just casts a pointer to the first element to a pointer to the whole struct. + static KDPairDec* RecastDataPointer(Data* data_ptr) { + return reinterpret_cast(data_ptr); + } +}; + +// A useful base class to facilitate the common operation of sorting a vector +// of owned pointer data using a separate key. This class owns its data pointer, +// deleting it when it has finished with it, and providing copy constructor and +// operator= that have move semantics so that the data does not get copied and +// only a single instance of KDPtrPair holds a specific data pointer. +template +class KDPtrPair { + public: + KDPtrPair() : data_(NULL) {} + KDPtrPair(Key k, Data* d) : data_(d), key_(k) {} + // Copy constructor steals the pointer from src and NULLs it in src, thereby + // moving the (single) ownership of the data. + KDPtrPair(KDPtrPair& src) : data_(src.data_), key_(src.key_) { + src.data_ = NULL; + } + // Destructor deletes data, assuming it is the sole owner. + ~KDPtrPair() { + delete this->data_; + this->data_ = NULL; + } + // Operator= steals the pointer from src and NULLs it in src, thereby + // moving the (single) ownership of the data. + void operator=(KDPtrPair& src) { + delete this->data_; + this->data_ = src.data_; + src.data_ = NULL; + this->key_ = src.key_; + } + + int operator==(const KDPtrPair& other) const { + return key_ == other.key_; + } + + // Accessors. + const Key& key() const { + return key_; + } + void set_key(const Key& new_key) { + key_ = new_key; + } + const Data* data() const { + return data_; + } + // Sets the data pointer, taking ownership of the data. + void set_data(Data* new_data) { + delete data_; + data_ = new_data; + } + // Relinquishes ownership of the data pointer (setting it to NULL). + Data* extract_data() { + Data* result = data_; + data_ = NULL; + return result; + } + + private: + // Data members are private to keep deletion of data_ encapsulated. + Data* data_; + Key key_; +}; +// Specialization of KDPtrPair to provide operator< for sorting in increasing +// order. +template +struct KDPtrPairInc : public KDPtrPair { + // Since we are doing non-standard stuff we have to duplicate *all* the + // constructors and operator=. + KDPtrPairInc() : KDPtrPair() {} + KDPtrPairInc(Key k, Data* d) : KDPtrPair(k, d) {} + KDPtrPairInc(KDPtrPairInc& src) : KDPtrPair(src) {} + void operator=(KDPtrPairInc& src) { + KDPtrPair::operator=(src); + } + // Operator< facilitates sorting in increasing order. + int operator<(const KDPtrPairInc& other) const { + return this->key() < other.key(); + } +}; +// Specialization of KDPtrPair to provide operator< for sorting in decreasing +// order. +template +struct KDPtrPairDec : public KDPtrPair { + // Since we are doing non-standard stuff we have to duplicate *all* the + // constructors and operator=. + KDPtrPairDec() : KDPtrPair() {} + KDPtrPairDec(Key k, Data* d) : KDPtrPair(k, d) {} + KDPtrPairDec(KDPtrPairDec& src) : KDPtrPair(src) {} + void operator=(KDPtrPairDec& src) { + KDPtrPair::operator=(src); + } + // Operator< facilitates sorting in decreasing order by using operator> on + // the key values. + int operator<(const KDPtrPairDec& other) const { + return this->key() > other.key(); + } +}; + +// Specialization for a pair of ints in increasing order. +typedef KDPairInc IntKDPair; + +// Vector of IntKDPair. +class KDVector : public GenericVector { + // TODO(rays) Add some code to manipulate a KDVector. For now there + // is nothing and this class is effectively a specialization typedef. +}; + +} // namespace tesseract + +#endif // TESSERACT_CCUTIL_KDPAIR_H_ diff --git a/ccutil/object_cache.h b/ccutil/object_cache.h new file mode 100644 index 0000000000..c8c3167cf1 --- /dev/null +++ b/ccutil/object_cache.h @@ -0,0 +1,125 @@ +/////////////////////////////////////////////////////////////////////// +// File: object_cache.h +// Description: A string indexed object cache. +// Author: David Eger +// Created: Fri Jan 27 12:08:00 PST 2012 +// +// (C) Copyright 2012, Google Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +/////////////////////////////////////////////////////////////////////// + +#ifndef TESSERACT_CCUTIL_OBJECT_CACHE_H_ +#define TESSERACT_CCUTIL_OBJECT_CACHE_H_ + +#include "ccutil.h" +#include "errcode.h" +#include "genericvector.h" +#include "tesscallback.h" + +namespace tesseract { + +// A simple object cache which maps a string to an object of type T. +// Usually, these are expensive objects that are loaded from disk. +// Reference counting is performed, so every Get() needs to be followed later +// by a Free(). Actual deletion is accomplished by DeleteUnusedObjects(). +template +class ObjectCache { + public: + ObjectCache() {} + ~ObjectCache() { + mu_.Lock(); + for (int i = 0; i < cache_.size(); i++) { + if (cache_[i].count > 0) { + tprintf("ObjectCache(%p)::~ObjectCache(): WARNING! LEAK! object %p " + "still has count %d (id %s)\n", + this, cache_[i].object, cache_[i].count, + cache_[i].id.string()); + } else { + delete cache_[i].object; + cache_[i].object = NULL; + } + } + mu_.Unlock(); + } + + // Return a pointer to the object identified by id. + // If we haven't yet loaded the object, use loader to load it. + // If loader fails to load it, record a NULL entry in the cache + // and return NULL -- further attempts to load will fail (even + // with a different loader) until DeleteUnusedObjects() is called. + // We delete the given loader. + T *Get(STRING id, + TessResultCallback *loader) { + T *retval = NULL; + mu_.Lock(); + for (int i = 0; i < cache_.size(); i++) { + if (id == cache_[i].id) { + retval = cache_[i].object; + if (cache_[i].object != NULL) { + cache_[i].count++; + } + mu_.Unlock(); + delete loader; + return retval; + } + } + cache_.push_back(ReferenceCount()); + ReferenceCount &rc = cache_.back(); + rc.id = id; + retval = rc.object = loader->Run(); + rc.count = (retval != NULL) ? 1 : 0; + mu_.Unlock(); + return retval; + } + + // Decrement the count for t. + // Return whether we knew about the given pointer. + bool Free(T *t) { + if (t == NULL) return false; + mu_.Lock(); + for (int i = 0; i < cache_.size(); i++) { + if (cache_[i].object == t) { + --cache_[i].count; + mu_.Unlock(); + return true; + } + } + mu_.Unlock(); + return false; + } + + void DeleteUnusedObjects() { + mu_.Lock(); + for (int i = cache_.size() - 1; i >= 0; i--) { + if (cache_[i].count <= 0) { + delete cache_[i].object; + cache_.remove(i); + } + } + mu_.Unlock(); + } + + private: + struct ReferenceCount { + STRING id; // A unique ID to identify the object (think path on disk) + T *object; // A copy of the object in memory. Can be delete'd. + int count; // A count of the number of active users of this object. + }; + + CCUtilMutex mu_; + GenericVector cache_; +}; + +} // namespace tesseract + + +#endif // TESSERACT_CCUTIL_OBJECT_CACHE_H_ diff --git a/ccutil/params.cpp b/ccutil/params.cpp index c7a6256320..8a43cd823d 100644 --- a/ccutil/params.cpp +++ b/ccutil/params.cpp @@ -207,4 +207,25 @@ void ParamUtils::PrintParams(FILE *fp, const ParamsVectors *member_params) { } } +// Resets all parameters back to default values; +void ParamUtils::ResetToDefaults(ParamsVectors* member_params) { + int v, i; + int num_iterations = (member_params == NULL) ? 1 : 2; + for (v = 0; v < num_iterations; ++v) { + ParamsVectors *vec = (v == 0) ? GlobalParams() : member_params; + for (i = 0; i < vec->int_params.size(); ++i) { + vec->int_params[i]->ResetToDefault(); + } + for (i = 0; i < vec->bool_params.size(); ++i) { + vec->bool_params[i]->ResetToDefault(); + } + for (int i = 0; i < vec->string_params.size(); ++i) { + vec->string_params[i]->ResetToDefault(); + } + for (int i = 0; i < vec->double_params.size(); ++i) { + vec->double_params[i]->ResetToDefault(); + } + } +} + } // namespace tesseract diff --git a/ccutil/params.h b/ccutil/params.h index f95ac6315f..8fc3653afd 100644 --- a/ccutil/params.h +++ b/ccutil/params.h @@ -104,6 +104,9 @@ class ParamUtils { // Print parameters to the given file. static void PrintParams(FILE *fp, const ParamsVectors *member_params); + + // Resets all parameters back to default values; + static void ResetToDefaults(ParamsVectors* member_params); }; // Definition of various parameter types. @@ -142,15 +145,20 @@ class IntParam : public Param { IntParam(inT32 value, const char *name, const char *comment, bool init, ParamsVectors *vec) : Param(name, comment, init) { value_ = value; + default_ = value; params_vec_ = &(vec->int_params); vec->int_params.push_back(this); } ~IntParam() { ParamUtils::RemoveParam(this, params_vec_); } operator inT32() const { return value_; } void set_value(inT32 value) { value_ = value; } + void ResetToDefault() { + value_ = default_; + } private: inT32 value_; + inT32 default_; // Pointer to the vector that contains this param (not owened by this class). GenericVector *params_vec_; }; @@ -160,15 +168,20 @@ class BoolParam : public Param { BoolParam(bool value, const char *name, const char *comment, bool init, ParamsVectors *vec) : Param(name, comment, init) { value_ = value; + default_ = value; params_vec_ = &(vec->bool_params); vec->bool_params.push_back(this); } ~BoolParam() { ParamUtils::RemoveParam(this, params_vec_); } operator BOOL8() const { return value_; } void set_value(BOOL8 value) { value_ = value; } + void ResetToDefault() { + value_ = default_; + } private: BOOL8 value_; + BOOL8 default_; // Pointer to the vector that contains this param (not owned by this class). GenericVector *params_vec_; }; @@ -179,17 +192,23 @@ class StringParam : public Param { const char *comment, bool init, ParamsVectors *vec) : Param(name, comment, init) { value_ = value; + default_ = value; params_vec_ = &(vec->string_params); vec->string_params.push_back(this); } ~StringParam() { ParamUtils::RemoveParam(this, params_vec_); } operator STRING &() { return value_; } const char *string() const { return value_.string(); } + const char *c_str() const { return value_.string(); } bool empty() { return value_.length() <= 0; } void set_value(const STRING &value) { value_ = value; } + void ResetToDefault() { + value_ = default_; + } private: STRING value_; + STRING default_; // Pointer to the vector that contains this param (not owened by this class). GenericVector *params_vec_; }; @@ -199,15 +218,20 @@ class DoubleParam : public Param { DoubleParam(double value, const char *name, const char *comment, bool init, ParamsVectors *vec) : Param(name, comment, init) { value_ = value; + default_ = value; params_vec_ = &(vec->double_params); vec->double_params.push_back(this); } ~DoubleParam() { ParamUtils::RemoveParam(this, params_vec_); } operator double() const { return value_; } void set_value(double value) { value_ = value; } + void ResetToDefault() { + value_ = default_; + } private: double value_; + double default_; // Pointer to the vector that contains this param (not owned by this class). GenericVector *params_vec_; }; diff --git a/ccutil/platform.h b/ccutil/platform.h index 961e985754..a50e9d78a7 100644 --- a/ccutil/platform.h +++ b/ccutil/platform.h @@ -20,16 +20,12 @@ #ifndef TESSERACT_CCUTIL_PLATFORM_H__ #define TESSERACT_CCUTIL_PLATFORM_H__ +#include + #define DLLSYM #ifdef _WIN32 #ifdef __GNUC__ #define ultoa _ultoa -#ifndef __MINGW32__ -typedef struct _BLOB { - unsigned int cbSize; - char *pBlobData; -} BLOB, *LPBLOB; -#endif /* __MINGW32__ */ #endif /* __GNUC__ */ #define SIGNED #define snprintf _snprintf @@ -71,4 +67,12 @@ typedef struct _BLOB { #endif #endif +#if defined(_WIN32) || defined(__CYGWIN__) + #define _TESS_FILE_BASENAME_ \ + (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__) +#else // Unices + #define _TESS_FILE_BASENAME_ \ + (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) +#endif + #endif // TESSERACT_CCUTIL_PLATFORM_H__ diff --git a/ccutil/sorthelper.h b/ccutil/sorthelper.h index 4e6542de9b..4da13b9229 100644 --- a/ccutil/sorthelper.h +++ b/ccutil/sorthelper.h @@ -56,7 +56,9 @@ class SortHelper { } // Constructor takes a hint of the array size, but it need not be accurate. - explicit SortHelper(int sizehint) : counts_(sizehint) {} + explicit SortHelper(int sizehint) { + counts_.reserve(sizehint); + } // Add a value that may be a duplicate of an existing value. // Uses a linear search. diff --git a/ccutil/strngs.cpp b/ccutil/strngs.cpp index f5c25856e4..4fbd371c8a 100644 --- a/ccutil/strngs.cpp +++ b/ccutil/strngs.cpp @@ -24,8 +24,11 @@ #include // Size of buffer needed to host the decimal representation of the maximum -// possible length of an int (in 64 bits, being -<20 digits>. +// possible length of an int (in 64 bits), being -<20 digits>. const int kMaxIntSize = 22; +// Size of buffer needed to host the decimal representation of the maximum +// possible length of a %.8g being -0.12345678e+999 = 15. +const int kMaxDoubleSize = 15; /********************************************************************** * STRING_HEADER provides metadata about the allocated buffer, @@ -163,6 +166,10 @@ const char* STRING::string() const { return GetCStr(); } +const char* STRING::c_str() const { + return string(); +} + /****** * The STRING_IS_PROTECTED interface adds additional support to migrate * code that needs to modify the STRING in ways not otherwise supported @@ -220,6 +227,8 @@ void STRING::erase_range(inT32 index, int len) { #else void STRING::truncate_at(inT32 index) { + ASSERT_HOST(index >= 0); + FixHeader(); char* this_cstr = ensure_cstr(index + 1); this_cstr[index] = '\0'; GetHeader()->used_ = index + 1; @@ -339,6 +348,16 @@ void STRING::add_str_int(const char* str, int number) { num_buffer[kMaxIntSize - 1] = '\0'; *this += num_buffer; } +// Appends the given string and double (as a %.8g) to this. +void STRING::add_str_double(const char* str, double number) { + if (str != NULL) + *this += str; + // Allow space for the maximum possible length of %8g. + char num_buffer[kMaxDoubleSize]; + snprintf(num_buffer, kMaxDoubleSize - 1, "%.8g", number); + num_buffer[kMaxDoubleSize - 1] = '\0'; + *this += num_buffer; +} STRING & STRING::operator=(const char* cstr) { STRING_HEADER* this_header = GetHeader(); diff --git a/ccutil/strngs.h b/ccutil/strngs.h index 04d7314dda..2943b531b6 100644 --- a/ccutil/strngs.h +++ b/ccutil/strngs.h @@ -55,6 +55,7 @@ class TESS_API STRING inT32 length() const; inT32 size() const { return length(); } const char *string() const; + const char *c_str() const; inline char* strdup() const { inT32 len = length() + 1; @@ -94,8 +95,10 @@ class TESS_API STRING // be ambiguous, and ints usually need a string before or between them // anyway. void add_str_int(const char* str, int number); + // Appends the given string and double (as a %.8g) to this. + void add_str_double(const char* str, double number); - // ensure capcaity but keep pointer encapsulated + // ensure capacity but keep pointer encapsulated inline void ensure(inT32 min_capacity) { ensure_cstr(min_capacity); } private: diff --git a/ccutil/tesscallback.h b/ccutil/tesscallback.h index b34cdb1461..1f20c6b474 100644 --- a/ccutil/tesscallback.h +++ b/ccutil/tesscallback.h @@ -270,43 +270,8537 @@ NewPermanentTessCallback(R (*function)()) { return new _TessFunctionResultCallback_0_0(function); } + + +// Specified by TR1 [4.7.2] Reference modifications. +template struct remove_reference; +template struct remove_reference { typedef T type; }; +template struct remove_reference { typedef T type; }; + +// Identity::type is a typedef of T. Useful for preventing the +// compiler from inferring the type of an argument in templates. +template +struct Identity { + typedef T type; +}; + +template +class _ConstTessMemberResultCallback_1_0 : public TessResultCallback { + public: + typedef TessResultCallback base; + typedef R (T::*MemberSignature)(P1) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _ConstTessMemberResultCallback_1_0(const T* object, MemberSignature member, P1 p1) + : object_(object), + member_(member), p1_(p1) { } + + virtual R Run() { + if (!del) { + R result = (object_->*member_)(p1_); + return result; + } else { + R result = (object_->*member_)(p1_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_1_0 : public TessClosure { + public: + typedef TessClosure base; + typedef void (T::*MemberSignature)(P1) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _ConstTessMemberResultCallback_1_0(const T* object, MemberSignature member, P1 p1) + : object_(object), + member_(member), p1_(p1) { } + + virtual void Run() { + if (!del) { + (object_->*member_)(p1_); + } else { + (object_->*member_)(p1_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_1_0::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1) const, typename Identity::type p1) { + return new _ConstTessMemberResultCallback_1_0(obj, member, p1); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_1_0::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1) const, typename Identity::type p1) { + return new _ConstTessMemberResultCallback_1_0(obj, member, p1); +} +#endif + +template +class _TessMemberResultCallback_1_0 : public TessResultCallback { + public: + typedef TessResultCallback base; + typedef R (T::*MemberSignature)(P1) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _TessMemberResultCallback_1_0( T* object, MemberSignature member, P1 p1) + : object_(object), + member_(member), p1_(p1) { } + + virtual R Run() { + if (!del) { + R result = (object_->*member_)(p1_); + return result; + } else { + R result = (object_->*member_)(p1_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_1_0 : public TessClosure { + public: + typedef TessClosure base; + typedef void (T::*MemberSignature)(P1) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _TessMemberResultCallback_1_0( T* object, MemberSignature member, P1 p1) + : object_(object), + member_(member), p1_(p1) { } + + virtual void Run() { + if (!del) { + (object_->*member_)(p1_); + } else { + (object_->*member_)(p1_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_1_0::base* +NewTessCallback( T1* obj, R (T2::*member)(P1) , typename Identity::type p1) { + return new _TessMemberResultCallback_1_0(obj, member, p1); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_1_0::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1) , typename Identity::type p1) { + return new _TessMemberResultCallback_1_0(obj, member, p1); +} +#endif + +template +class _TessFunctionResultCallback_1_0 : public TessResultCallback { + public: + typedef TessResultCallback base; + typedef R (*FunctionSignature)(P1); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + + public: + inline _TessFunctionResultCallback_1_0(FunctionSignature function, P1 p1) + : function_(function), p1_(p1) { } + + virtual R Run() { + if (!del) { + R result = (*function_)(p1_); + return result; + } else { + R result = (*function_)(p1_); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_1_0 : public TessClosure { + public: + typedef TessClosure base; + typedef void (*FunctionSignature)(P1); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + + public: + inline _TessFunctionResultCallback_1_0(FunctionSignature function, P1 p1) + : function_(function), p1_(p1) { } + + virtual void Run() { + if (!del) { + (*function_)(p1_); + } else { + (*function_)(p1_); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_1_0::base* +NewTessCallback(R (*function)(P1), typename Identity::type p1) { + return new _TessFunctionResultCallback_1_0(function, p1); +} + +template +inline typename _TessFunctionResultCallback_1_0::base* +NewPermanentTessCallback(R (*function)(P1), typename Identity::type p1) { + return new _TessFunctionResultCallback_1_0(function, p1); +} + +template +class _ConstTessMemberResultCallback_2_0 : public TessResultCallback { + public: + typedef TessResultCallback base; + typedef R (T::*MemberSignature)(P1,P2) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _ConstTessMemberResultCallback_2_0(const T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual R Run() { + if (!del) { + R result = (object_->*member_)(p1_,p2_); + return result; + } else { + R result = (object_->*member_)(p1_,p2_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_2_0 : public TessClosure { + public: + typedef TessClosure base; + typedef void (T::*MemberSignature)(P1,P2) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _ConstTessMemberResultCallback_2_0(const T* object, MemberSignature member, P1 p1, P2 p2) + : + object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual void Run() { + if (!del) { + (object_->*member_)(p1_,p2_); + } else { + (object_->*member_)(p1_,p2_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_2_0::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2) const, typename Identity::type p1, typename Identity::type p2) { + return new _ConstTessMemberResultCallback_2_0(obj, member, p1, p2); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_2_0::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2) const, typename Identity::type p1, typename Identity::type p2) { + return new _ConstTessMemberResultCallback_2_0(obj, member, p1, p2); +} +#endif + +template +class _TessMemberResultCallback_2_0 : public TessResultCallback { + public: + typedef TessResultCallback base; + typedef R (T::*MemberSignature)(P1,P2) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessMemberResultCallback_2_0( T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual R Run() { + if (!del) { + R result = (object_->*member_)(p1_,p2_); + return result; + } else { + R result = (object_->*member_)(p1_,p2_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_2_0 : public TessClosure { + public: + typedef TessClosure base; + typedef void (T::*MemberSignature)(P1,P2) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessMemberResultCallback_2_0( T* object, MemberSignature member, P1 p1, P2 p2) + : + object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual void Run() { + if (!del) { + (object_->*member_)(p1_,p2_); + } else { + (object_->*member_)(p1_,p2_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_2_0::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2) , typename Identity::type p1, typename Identity::type p2) { + return new _TessMemberResultCallback_2_0(obj, member, p1, p2); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_2_0::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2) , typename Identity::type p1, typename Identity::type p2) { + return new _TessMemberResultCallback_2_0(obj, member, p1, p2); +} +#endif + +template +class _TessFunctionResultCallback_2_0 : public TessResultCallback { + public: + typedef TessResultCallback base; + typedef R (*FunctionSignature)(P1,P2); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessFunctionResultCallback_2_0(FunctionSignature function, P1 p1, P2 p2) + : function_(function), p1_(p1), p2_(p2) { } + + virtual R Run() { + if (!del) { + R result = (*function_)(p1_,p2_); + return result; + } else { + R result = (*function_)(p1_,p2_); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_2_0 : public TessClosure { + public: + typedef TessClosure base; + typedef void (*FunctionSignature)(P1,P2); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessFunctionResultCallback_2_0(FunctionSignature function, P1 p1, P2 p2) + : + function_(function), p1_(p1), p2_(p2) { } + + virtual void Run() { + if (!del) { + (*function_)(p1_,p2_); + } else { + (*function_)(p1_,p2_); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_2_0::base* +NewTessCallback(R (*function)(P1,P2), typename Identity::type p1, typename Identity::type p2) { + return new _TessFunctionResultCallback_2_0(function, p1, p2); +} + +template +inline typename _TessFunctionResultCallback_2_0::base* +NewPermanentTessCallback(R (*function)(P1,P2), typename Identity::type p1, typename Identity::type p2) { + return new _TessFunctionResultCallback_2_0(function, p1, p2); +} + +template +class _ConstTessMemberResultCallback_3_0 : public TessResultCallback { + public: + typedef TessResultCallback base; + typedef R (T::*MemberSignature)(P1,P2,P3) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _ConstTessMemberResultCallback_3_0(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : + object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual R Run() { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_3_0 : public TessClosure { + public: + typedef TessClosure base; + typedef void (T::*MemberSignature)(P1,P2,P3) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _ConstTessMemberResultCallback_3_0(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual void Run() { + if (!del) { + (object_->*member_)(p1_,p2_,p3_); + } else { + (object_->*member_)(p1_,p2_,p3_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_3_0::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _ConstTessMemberResultCallback_3_0(obj, member, p1, p2, p3); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_3_0::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _ConstTessMemberResultCallback_3_0(obj, member, p1, p2, p3); +} +#endif + +template +class _TessMemberResultCallback_3_0 : public TessResultCallback { + public: + typedef TessResultCallback base; + typedef R (T::*MemberSignature)(P1,P2,P3) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessMemberResultCallback_3_0( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual R Run() { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_3_0 : public TessClosure { + public: + typedef TessClosure base; + typedef void (T::*MemberSignature)(P1,P2,P3) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessMemberResultCallback_3_0( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual void Run() { + if (!del) { + (object_->*member_)(p1_,p2_,p3_); + } else { + (object_->*member_)(p1_,p2_,p3_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_3_0::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessMemberResultCallback_3_0(obj, member, p1, p2, p3); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_3_0::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessMemberResultCallback_3_0(obj, member, p1, p2, p3); +} +#endif + +template +class _TessFunctionResultCallback_3_0 : public TessResultCallback { + public: + typedef TessResultCallback base; + typedef R (*FunctionSignature)(P1,P2,P3); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessFunctionResultCallback_3_0(FunctionSignature function, P1 p1, P2 p2, P3 p3) + : function_(function), p1_(p1), p2_(p2), p3_(p3) { } + + virtual R Run() { + if (!del) { + R result = (*function_)(p1_,p2_,p3_); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_3_0 : public TessClosure { + public: + typedef TessClosure base; + typedef void (*FunctionSignature)(P1,P2,P3); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessFunctionResultCallback_3_0(FunctionSignature function, P1 p1, P2 p2, P3 p3) + : function_(function), p1_(p1), p2_(p2), p3_(p3) { } + + virtual void Run() { + if (!del) { + (*function_)(p1_,p2_,p3_); + } else { + (*function_)(p1_,p2_,p3_); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_3_0::base* +NewTessCallback(R (*function)(P1,P2,P3), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessFunctionResultCallback_3_0(function, p1, p2, p3); +} + +template +inline typename _TessFunctionResultCallback_3_0::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessFunctionResultCallback_3_0(function, p1, p2, p3); +} + +template +class _ConstTessMemberResultCallback_4_0 : public TessResultCallback { + public: + typedef TessResultCallback base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _ConstTessMemberResultCallback_4_0(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual R Run() { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_4_0 : public TessClosure { + public: + typedef TessClosure base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _ConstTessMemberResultCallback_4_0(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual void Run() { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_4_0::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _ConstTessMemberResultCallback_4_0(obj, member, p1, p2, p3, p4); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_4_0::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _ConstTessMemberResultCallback_4_0(obj, member, p1, p2, p3, p4); +} +#endif + +template +class _TessMemberResultCallback_4_0 : public TessResultCallback { + public: + typedef TessResultCallback base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessMemberResultCallback_4_0( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual R Run() { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_4_0 : public TessClosure { + public: + typedef TessClosure base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessMemberResultCallback_4_0( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual void Run() { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_4_0::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessMemberResultCallback_4_0(obj, member, p1, p2, p3, p4); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_4_0::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessMemberResultCallback_4_0(obj, member, p1, p2, p3, p4); +} +#endif + +template +class _TessFunctionResultCallback_4_0 : public TessResultCallback { + public: + typedef TessResultCallback base; + typedef R (*FunctionSignature)(P1,P2,P3,P4); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessFunctionResultCallback_4_0(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual R Run() { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,p4_); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,p4_); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_4_0 : public TessClosure { + public: + typedef TessClosure base; + typedef void (*FunctionSignature)(P1,P2,P3,P4); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessFunctionResultCallback_4_0(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual void Run() { + if (!del) { + (*function_)(p1_,p2_,p3_,p4_); + } else { + (*function_)(p1_,p2_,p3_,p4_); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_4_0::base* +NewTessCallback(R (*function)(P1,P2,P3,P4), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessFunctionResultCallback_4_0(function, p1, p2, p3, p4); +} + +template +inline typename _TessFunctionResultCallback_4_0::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,P4), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessFunctionResultCallback_4_0(function, p1, p2, p3, p4); +} + +template +class _ConstTessMemberResultCallback_5_0 : public TessResultCallback { + public: + typedef TessResultCallback base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _ConstTessMemberResultCallback_5_0(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual R Run() { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_5_0 : public TessClosure { + public: + typedef TessClosure base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _ConstTessMemberResultCallback_5_0(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual void Run() { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_5_0::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _ConstTessMemberResultCallback_5_0(obj, member, p1, p2, p3, p4, p5); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_5_0::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _ConstTessMemberResultCallback_5_0(obj, member, p1, p2, p3, p4, p5); +} +#endif + +template +class _TessMemberResultCallback_5_0 : public TessResultCallback { + public: + typedef TessResultCallback base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessMemberResultCallback_5_0( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual R Run() { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_5_0 : public TessClosure { + public: + typedef TessClosure base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessMemberResultCallback_5_0( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual void Run() { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_5_0::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessMemberResultCallback_5_0(obj, member, p1, p2, p3, p4, p5); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_5_0::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessMemberResultCallback_5_0(obj, member, p1, p2, p3, p4, p5); +} +#endif + +template +class _TessFunctionResultCallback_5_0 : public TessResultCallback { + public: + typedef TessResultCallback base; + typedef R (*FunctionSignature)(P1,P2,P3,P4,P5); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessFunctionResultCallback_5_0(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual R Run() { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_5_0 : public TessClosure { + public: + typedef TessClosure base; + typedef void (*FunctionSignature)(P1,P2,P3,P4,P5); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessFunctionResultCallback_5_0(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual void Run() { + if (!del) { + (*function_)(p1_,p2_,p3_,p4_,p5_); + } else { + (*function_)(p1_,p2_,p3_,p4_,p5_); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_5_0::base* +NewTessCallback(R (*function)(P1,P2,P3,P4,P5), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessFunctionResultCallback_5_0(function, p1, p2, p3, p4, p5); +} + +template +inline typename _TessFunctionResultCallback_5_0::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,P4,P5), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessFunctionResultCallback_5_0(function, p1, p2, p3, p4, p5); +} + +template +class _ConstTessMemberResultCallback_6_0 : public TessResultCallback { + public: + typedef TessResultCallback base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,P6) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _ConstTessMemberResultCallback_6_0(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual R Run() { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_6_0 : public TessClosure { + public: + typedef TessClosure base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,P6) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _ConstTessMemberResultCallback_6_0(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual void Run() { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_6_0::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _ConstTessMemberResultCallback_6_0(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_6_0::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _ConstTessMemberResultCallback_6_0(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +template +class _TessMemberResultCallback_6_0 : public TessResultCallback { + public: + typedef TessResultCallback base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,P6) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessMemberResultCallback_6_0( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual R Run() { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_6_0 : public TessClosure { + public: + typedef TessClosure base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,P6) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessMemberResultCallback_6_0( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual void Run() { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_6_0::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessMemberResultCallback_6_0(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_6_0::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessMemberResultCallback_6_0(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +template +class _TessFunctionResultCallback_6_0 : public TessResultCallback { + public: + typedef TessResultCallback base; + typedef R (*FunctionSignature)(P1,P2,P3,P4,P5,P6); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessFunctionResultCallback_6_0(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual R Run() { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,p6_); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,p6_); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_6_0 : public TessClosure { + public: + typedef TessClosure base; + typedef void (*FunctionSignature)(P1,P2,P3,P4,P5,P6); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessFunctionResultCallback_6_0(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual void Run() { + if (!del) { + (*function_)(p1_,p2_,p3_,p4_,p5_,p6_); + } else { + (*function_)(p1_,p2_,p3_,p4_,p5_,p6_); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_6_0::base* +NewTessCallback(R (*function)(P1,P2,P3,P4,P5,P6), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessFunctionResultCallback_6_0(function, p1, p2, p3, p4, p5, p6); +} + +template +inline typename _TessFunctionResultCallback_6_0::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,P4,P5,P6), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessFunctionResultCallback_6_0(function, p1, p2, p3, p4, p5, p6); +} + template class TessCallback1 { public: - virtual ~TessCallback1() { } - virtual void Run(A1) = 0; + virtual ~TessCallback1() { } + virtual void Run(A1) = 0; +}; + +template +class TessResultCallback1 { + public: + virtual ~TessResultCallback1() { } + virtual R Run(A1) = 0; +}; + + +template +class TessCallback2 { + public: + virtual ~TessCallback2() { } + virtual void Run(A1,A2) = 0; +}; + +template +class TessResultCallback2 { + public: + virtual ~TessResultCallback2() { } + virtual R Run(A1,A2) = 0; +}; + +template +class TessCallback3 { + public: + virtual ~TessCallback3() { } + virtual void Run(A1,A2,A3) = 0; +}; + +template +class TessResultCallback3 { + public: + virtual ~TessResultCallback3() { } + virtual R Run(A1,A2,A3) = 0; +}; + +template +class TessCallback4 { + public: + virtual ~TessCallback4() { } + virtual void Run(A1,A2,A3,A4) = 0; +}; + +template +class TessResultCallback4 { + public: + virtual ~TessResultCallback4() { } + virtual R Run(A1,A2,A3,A4) = 0; +}; + +template +class _ConstTessMemberResultCallback_0_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (T::*MemberSignature)(A1) const; + + private: + const T* object_; + MemberSignature member_; + + public: + inline _ConstTessMemberResultCallback_0_1( + const T* object, MemberSignature member) + : object_(object), + member_(member) { + } + + virtual R Run(A1 a1) { + if (!del) { + R result = (object_->*member_)(a1); + return result; + } else { + R result = (object_->*member_)(a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_0_1 + : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (T::*MemberSignature)(A1) const; + + private: + const T* object_; + MemberSignature member_; + + public: + inline _ConstTessMemberResultCallback_0_1( + const T* object, MemberSignature member) + : object_(object), + member_(member) { + } + + virtual void Run(A1 a1) { + if (!del) { + (object_->*member_)(a1); + } else { + (object_->*member_)(a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_0_1::base* +NewTessCallback( + const T1* obj, R (T2::*member)(A1) const) { + return new _ConstTessMemberResultCallback_0_1( + obj, member); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_0_1::base* +NewPermanentTessCallback( + const T1* obj, R (T2::*member)(A1) const) { + return new _ConstTessMemberResultCallback_0_1( + obj, member); +} +#endif + +template +class _TessMemberResultCallback_0_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (T::*MemberSignature)(A1) ; + + private: + T* object_; + MemberSignature member_; + + public: + inline _TessMemberResultCallback_0_1( + T* object, MemberSignature member) + : object_(object), + member_(member) { + } + + virtual R Run(A1 a1) { + if (!del) { + R result = (object_->*member_)(a1); + return result; + } else { + R result = (object_->*member_)(a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_0_1 + : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (T::*MemberSignature)(A1) ; + + private: + T* object_; + MemberSignature member_; + + public: + inline _TessMemberResultCallback_0_1( + T* object, MemberSignature member) + : object_(object), + member_(member) { + } + + virtual void Run(A1 a1) { + if (!del) { + (object_->*member_)(a1); + } else { + (object_->*member_)(a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_0_1::base* +NewTessCallback( + T1* obj, R (T2::*member)(A1) ) { + return new _TessMemberResultCallback_0_1( + obj, member); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_0_1::base* +NewPermanentTessCallback( + T1* obj, R (T2::*member)(A1) ) { + return new _TessMemberResultCallback_0_1( + obj, member); +} +#endif + +template +class _TessFunctionResultCallback_0_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (*FunctionSignature)(A1); + + private: + FunctionSignature function_; + + public: + inline _TessFunctionResultCallback_0_1( + FunctionSignature function) + : function_(function) { + } + + virtual R Run(A1 a1) { + if (!del) { + R result = (*function_)(a1); + return result; + } else { + R result = (*function_)(a1); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_0_1 + : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (*FunctionSignature)(A1); + + private: + FunctionSignature function_; + + public: + inline _TessFunctionResultCallback_0_1( + FunctionSignature function) + : function_(function) { + } + + virtual void Run(A1 a1) { + if (!del) { + (*function_)(a1); + } else { + (*function_)(a1); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_0_1::base* +NewTessCallback(R (*function)(A1)) { + return new _TessFunctionResultCallback_0_1(function); +} + +template +inline typename _TessFunctionResultCallback_0_1::base* +NewPermanentTessCallback(R (*function)(A1)) { + return new _TessFunctionResultCallback_0_1(function); +} + +template +class _ConstTessMemberResultCallback_1_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (T::*MemberSignature)(P1,A1) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _ConstTessMemberResultCallback_1_1(const T* object, MemberSignature member, P1 p1) + : object_(object), + member_(member), p1_(p1) { } + + virtual R Run(A1 a1) { + if (!del) { + R result = (object_->*member_)(p1_,a1); + return result; + } else { + R result = (object_->*member_)(p1_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_1_1 : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (T::*MemberSignature)(P1,A1) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _ConstTessMemberResultCallback_1_1(const T* object, MemberSignature member, P1 p1) + : object_(object), + member_(member), p1_(p1) { } + + virtual void Run(A1 a1) { + if (!del) { + (object_->*member_)(p1_,a1); + } else { + (object_->*member_)(p1_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_1_1::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,A1) const, typename Identity::type p1) { + return new _ConstTessMemberResultCallback_1_1(obj, member, p1); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_1_1::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,A1) const, typename Identity::type p1) { + return new _ConstTessMemberResultCallback_1_1(obj, member, p1); +} +#endif + +template +class _TessMemberResultCallback_1_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (T::*MemberSignature)(P1,A1) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _TessMemberResultCallback_1_1( T* object, MemberSignature member, P1 p1) + : object_(object), + member_(member), p1_(p1) { } + + virtual R Run(A1 a1) { + if (!del) { + R result = (object_->*member_)(p1_,a1); + return result; + } else { + R result = (object_->*member_)(p1_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_1_1 : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (T::*MemberSignature)(P1,A1) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _TessMemberResultCallback_1_1( T* object, MemberSignature member, P1 p1) + : object_(object), + member_(member), p1_(p1) { } + + virtual void Run(A1 a1) { + if (!del) { + (object_->*member_)(p1_,a1); + } else { + (object_->*member_)(p1_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_1_1::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,A1) , typename Identity::type p1) { + return new _TessMemberResultCallback_1_1(obj, member, p1); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_1_1::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,A1) , typename Identity::type p1) { + return new _TessMemberResultCallback_1_1(obj, member, p1); +} +#endif + +template +class _TessFunctionResultCallback_1_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (*FunctionSignature)(P1,A1); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + + public: + inline _TessFunctionResultCallback_1_1(FunctionSignature function, P1 p1) + : function_(function), p1_(p1) { } + + virtual R Run(A1 a1) { + if (!del) { + R result = (*function_)(p1_,a1); + return result; + } else { + R result = (*function_)(p1_,a1); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_1_1 : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (*FunctionSignature)(P1,A1); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + + public: + inline _TessFunctionResultCallback_1_1(FunctionSignature function, P1 p1) + : function_(function), p1_(p1) { } + + virtual void Run(A1 a1) { + if (!del) { + (*function_)(p1_,a1); + } else { + (*function_)(p1_,a1); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_1_1::base* +NewTessCallback(R (*function)(P1,A1), typename Identity::type p1) { + return new _TessFunctionResultCallback_1_1(function, p1); +} + +template +inline typename _TessFunctionResultCallback_1_1::base* +NewPermanentTessCallback(R (*function)(P1,A1), typename Identity::type p1) { + return new _TessFunctionResultCallback_1_1(function, p1); +} + +template +class _ConstTessMemberResultCallback_2_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (T::*MemberSignature)(P1,P2,A1) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _ConstTessMemberResultCallback_2_1(const T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual R Run(A1 a1) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,a1); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_2_1 : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (T::*MemberSignature)(P1,P2,A1) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _ConstTessMemberResultCallback_2_1(const T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual void Run(A1 a1) { + if (!del) { + (object_->*member_)(p1_,p2_,a1); + } else { + (object_->*member_)(p1_,p2_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_2_1::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,A1) const, typename Identity::type p1, typename Identity::type p2) { + return new _ConstTessMemberResultCallback_2_1(obj, member, p1, p2); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_2_1::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,A1) const, typename Identity::type p1, typename Identity::type p2) { + return new _ConstTessMemberResultCallback_2_1(obj, member, p1, p2); +} +#endif + +template +class _TessMemberResultCallback_2_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (T::*MemberSignature)(P1,P2,A1) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessMemberResultCallback_2_1( T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual R Run(A1 a1) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,a1); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_2_1 : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (T::*MemberSignature)(P1,P2,A1) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessMemberResultCallback_2_1( T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual void Run(A1 a1) { + if (!del) { + (object_->*member_)(p1_,p2_,a1); + } else { + (object_->*member_)(p1_,p2_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_2_1::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,A1) , typename Identity::type p1, typename Identity::type p2) { + return new _TessMemberResultCallback_2_1(obj, member, p1, p2); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_2_1::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,A1) , typename Identity::type p1, typename Identity::type p2) { + return new _TessMemberResultCallback_2_1(obj, member, p1, p2); +} +#endif + +template +class _TessFunctionResultCallback_2_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (*FunctionSignature)(P1,P2,A1); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessFunctionResultCallback_2_1(FunctionSignature function, P1 p1, P2 p2) + : function_(function), p1_(p1), p2_(p2) { } + + virtual R Run(A1 a1) { + if (!del) { + R result = (*function_)(p1_,p2_,a1); + return result; + } else { + R result = (*function_)(p1_,p2_,a1); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_2_1 : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (*FunctionSignature)(P1,P2,A1); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessFunctionResultCallback_2_1(FunctionSignature function, P1 p1, P2 p2) + : function_(function), p1_(p1), p2_(p2) { } + + virtual void Run(A1 a1) { + if (!del) { + (*function_)(p1_,p2_,a1); + } else { + (*function_)(p1_,p2_,a1); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_2_1::base* +NewTessCallback(R (*function)(P1,P2,A1), typename Identity::type p1, typename Identity::type p2) { + return new _TessFunctionResultCallback_2_1(function, p1, p2); +} + +template +inline typename _TessFunctionResultCallback_2_1::base* +NewPermanentTessCallback(R (*function)(P1,P2,A1), typename Identity::type p1, typename Identity::type p2) { + return new _TessFunctionResultCallback_2_1(function, p1, p2); +} + +template +class _ConstTessMemberResultCallback_3_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (T::*MemberSignature)(P1,P2,P3,A1) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _ConstTessMemberResultCallback_3_1(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual R Run(A1 a1) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,a1); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_3_1 : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (T::*MemberSignature)(P1,P2,P3,A1) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _ConstTessMemberResultCallback_3_1(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual void Run(A1 a1) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,a1); + } else { + (object_->*member_)(p1_,p2_,p3_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_3_1::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,A1) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _ConstTessMemberResultCallback_3_1(obj, member, p1, p2, p3); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_3_1::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,A1) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _ConstTessMemberResultCallback_3_1(obj, member, p1, p2, p3); +} +#endif + +template +class _TessMemberResultCallback_3_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (T::*MemberSignature)(P1,P2,P3,A1) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessMemberResultCallback_3_1( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual R Run(A1 a1) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,a1); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_3_1 : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (T::*MemberSignature)(P1,P2,P3,A1) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessMemberResultCallback_3_1( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual void Run(A1 a1) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,a1); + } else { + (object_->*member_)(p1_,p2_,p3_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_3_1::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,A1) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessMemberResultCallback_3_1(obj, member, p1, p2, p3); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_3_1::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,A1) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessMemberResultCallback_3_1(obj, member, p1, p2, p3); +} +#endif + +template +class _TessFunctionResultCallback_3_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (*FunctionSignature)(P1,P2,P3,A1); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessFunctionResultCallback_3_1(FunctionSignature function, P1 p1, P2 p2, P3 p3) + : function_(function), p1_(p1), p2_(p2), p3_(p3) { } + + virtual R Run(A1 a1) { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,a1); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,a1); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_3_1 : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (*FunctionSignature)(P1,P2,P3,A1); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessFunctionResultCallback_3_1(FunctionSignature function, P1 p1, P2 p2, P3 p3) + : function_(function), p1_(p1), p2_(p2), p3_(p3) { } + + virtual void Run(A1 a1) { + if (!del) { + (*function_)(p1_,p2_,p3_,a1); + } else { + (*function_)(p1_,p2_,p3_,a1); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_3_1::base* +NewTessCallback(R (*function)(P1,P2,P3,A1), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessFunctionResultCallback_3_1(function, p1, p2, p3); +} + +template +inline typename _TessFunctionResultCallback_3_1::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,A1), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessFunctionResultCallback_3_1(function, p1, p2, p3); +} + +template +class _ConstTessMemberResultCallback_4_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,A1) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _ConstTessMemberResultCallback_4_1(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual R Run(A1 a1) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_4_1 : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,A1) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _ConstTessMemberResultCallback_4_1(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual void Run(A1 a1) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,a1); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_4_1::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,A1) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _ConstTessMemberResultCallback_4_1(obj, member, p1, p2, p3, p4); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_4_1::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,A1) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _ConstTessMemberResultCallback_4_1(obj, member, p1, p2, p3, p4); +} +#endif + +template +class _TessMemberResultCallback_4_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,A1) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessMemberResultCallback_4_1( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual R Run(A1 a1) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_4_1 : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,A1) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessMemberResultCallback_4_1( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual void Run(A1 a1) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,a1); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_4_1::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,A1) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessMemberResultCallback_4_1(obj, member, p1, p2, p3, p4); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_4_1::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,A1) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessMemberResultCallback_4_1(obj, member, p1, p2, p3, p4); +} +#endif + +template +class _TessFunctionResultCallback_4_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (*FunctionSignature)(P1,P2,P3,P4,A1); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessFunctionResultCallback_4_1(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual R Run(A1 a1) { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,p4_,a1); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,p4_,a1); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_4_1 : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (*FunctionSignature)(P1,P2,P3,P4,A1); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessFunctionResultCallback_4_1(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual void Run(A1 a1) { + if (!del) { + (*function_)(p1_,p2_,p3_,p4_,a1); + } else { + (*function_)(p1_,p2_,p3_,p4_,a1); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_4_1::base* +NewTessCallback(R (*function)(P1,P2,P3,P4,A1), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessFunctionResultCallback_4_1(function, p1, p2, p3, p4); +} + +template +inline typename _TessFunctionResultCallback_4_1::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,P4,A1), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessFunctionResultCallback_4_1(function, p1, p2, p3, p4); +} + +template +class _ConstTessMemberResultCallback_5_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,A1) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _ConstTessMemberResultCallback_5_1(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual R Run(A1 a1) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_5_1 : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,A1) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _ConstTessMemberResultCallback_5_1(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual void Run(A1 a1) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_5_1::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,A1) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _ConstTessMemberResultCallback_5_1(obj, member, p1, p2, p3, p4, p5); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_5_1::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,A1) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _ConstTessMemberResultCallback_5_1(obj, member, p1, p2, p3, p4, p5); +} +#endif + +template +class _TessMemberResultCallback_5_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,A1) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessMemberResultCallback_5_1( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual R Run(A1 a1) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_5_1 : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,A1) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessMemberResultCallback_5_1( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual void Run(A1 a1) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_5_1::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,A1) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessMemberResultCallback_5_1(obj, member, p1, p2, p3, p4, p5); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_5_1::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,A1) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessMemberResultCallback_5_1(obj, member, p1, p2, p3, p4, p5); +} +#endif + +template +class _TessFunctionResultCallback_5_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (*FunctionSignature)(P1,P2,P3,P4,P5,A1); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessFunctionResultCallback_5_1(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual R Run(A1 a1) { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,a1); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,a1); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_5_1 : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (*FunctionSignature)(P1,P2,P3,P4,P5,A1); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessFunctionResultCallback_5_1(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual void Run(A1 a1) { + if (!del) { + (*function_)(p1_,p2_,p3_,p4_,p5_,a1); + } else { + (*function_)(p1_,p2_,p3_,p4_,p5_,a1); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_5_1::base* +NewTessCallback(R (*function)(P1,P2,P3,P4,P5,A1), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessFunctionResultCallback_5_1(function, p1, p2, p3, p4, p5); +} + +template +inline typename _TessFunctionResultCallback_5_1::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,P4,P5,A1), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessFunctionResultCallback_5_1(function, p1, p2, p3, p4, p5); +} + +template +class _ConstTessMemberResultCallback_6_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,A1) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _ConstTessMemberResultCallback_6_1(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual R Run(A1 a1) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_6_1 : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,A1) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _ConstTessMemberResultCallback_6_1(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual void Run(A1 a1) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_6_1::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6,A1) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _ConstTessMemberResultCallback_6_1(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_6_1::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6,A1) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _ConstTessMemberResultCallback_6_1(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +template +class _TessMemberResultCallback_6_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,A1) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessMemberResultCallback_6_1( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual R Run(A1 a1) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_6_1 : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,A1) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessMemberResultCallback_6_1( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual void Run(A1 a1) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_6_1::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6,A1) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessMemberResultCallback_6_1(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_6_1::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6,A1) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessMemberResultCallback_6_1(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +template +class _TessFunctionResultCallback_6_1 : public TessResultCallback1 { + public: + typedef TessResultCallback1 base; + typedef R (*FunctionSignature)(P1,P2,P3,P4,P5,P6,A1); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessFunctionResultCallback_6_1(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual R Run(A1 a1) { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_6_1 : public TessCallback1 { + public: + typedef TessCallback1 base; + typedef void (*FunctionSignature)(P1,P2,P3,P4,P5,P6,A1); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessFunctionResultCallback_6_1(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual void Run(A1 a1) { + if (!del) { + (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1); + } else { + (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_6_1::base* +NewTessCallback(R (*function)(P1,P2,P3,P4,P5,P6,A1), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessFunctionResultCallback_6_1(function, p1, p2, p3, p4, p5, p6); +} + +template +inline typename _TessFunctionResultCallback_6_1::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,P4,P5,P6,A1), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessFunctionResultCallback_6_1(function, p1, p2, p3, p4, p5, p6); +} + +template +class _ConstTessMemberResultCallback_0_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (T::*MemberSignature)(A1,A2) const; + + private: + const T* object_; + MemberSignature member_; + + public: + inline _ConstTessMemberResultCallback_0_2( + const T* object, MemberSignature member) + : object_(object), + member_(member) { + } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (object_->*member_)(a1,a2); + return result; + } else { + R result = (object_->*member_)(a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_0_2 + : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (T::*MemberSignature)(A1,A2) const; + + private: + const T* object_; + MemberSignature member_; + + public: + inline _ConstTessMemberResultCallback_0_2( + const T* object, MemberSignature member) + : object_(object), + member_(member) { + } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (object_->*member_)(a1,a2); + } else { + (object_->*member_)(a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_0_2::base* +NewTessCallback( + const T1* obj, R (T2::*member)(A1,A2) const) { + return new _ConstTessMemberResultCallback_0_2( + obj, member); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_0_2::base* +NewPermanentTessCallback( + const T1* obj, R (T2::*member)(A1,A2) const) { + return new _ConstTessMemberResultCallback_0_2( + obj, member); +} +#endif + +template +class _TessMemberResultCallback_0_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (T::*MemberSignature)(A1,A2) ; + + private: + T* object_; + MemberSignature member_; + + public: + inline _TessMemberResultCallback_0_2( + T* object, MemberSignature member) + : object_(object), + member_(member) { + } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (object_->*member_)(a1,a2); + return result; + } else { + R result = (object_->*member_)(a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_0_2 + : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (T::*MemberSignature)(A1,A2) ; + + private: + T* object_; + MemberSignature member_; + + public: + inline _TessMemberResultCallback_0_2( + T* object, MemberSignature member) + : object_(object), + member_(member) { + } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (object_->*member_)(a1,a2); + } else { + (object_->*member_)(a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_0_2::base* +NewTessCallback( + T1* obj, R (T2::*member)(A1,A2) ) { + return new _TessMemberResultCallback_0_2( + obj, member); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_0_2::base* +NewPermanentTessCallback( + T1* obj, R (T2::*member)(A1,A2) ) { + return new _TessMemberResultCallback_0_2( + obj, member); +} +#endif + +template +class _TessFunctionResultCallback_0_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (*FunctionSignature)(A1,A2); + + private: + FunctionSignature function_; + + public: + inline _TessFunctionResultCallback_0_2( + FunctionSignature function) + : function_(function) { + } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (*function_)(a1,a2); + return result; + } else { + R result = (*function_)(a1,a2); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_0_2 + : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (*FunctionSignature)(A1,A2); + + private: + FunctionSignature function_; + + public: + inline _TessFunctionResultCallback_0_2( + FunctionSignature function) + : function_(function) { + } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (*function_)(a1,a2); + } else { + (*function_)(a1,a2); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_0_2::base* +NewTessCallback(R (*function)(A1,A2)) { + return new _TessFunctionResultCallback_0_2(function); +} + +template +inline typename _TessFunctionResultCallback_0_2::base* +NewPermanentTessCallback(R (*function)(A1,A2)) { + return new _TessFunctionResultCallback_0_2(function); +} + +template +class _ConstTessMemberResultCallback_1_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (T::*MemberSignature)(P1,A1,A2) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _ConstTessMemberResultCallback_1_2(const T* object, MemberSignature member, P1 p1) + : object_(object), + member_(member), p1_(p1) { } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (object_->*member_)(p1_,a1,a2); + return result; + } else { + R result = (object_->*member_)(p1_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_1_2 : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (T::*MemberSignature)(P1,A1,A2) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _ConstTessMemberResultCallback_1_2(const T* object, MemberSignature member, P1 p1) + : object_(object), + member_(member), p1_(p1) { } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (object_->*member_)(p1_,a1,a2); + } else { + (object_->*member_)(p1_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_1_2::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,A1,A2) const, typename Identity::type p1) { + return new _ConstTessMemberResultCallback_1_2(obj, member, p1); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_1_2::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,A1,A2) const, typename Identity::type p1) { + return new _ConstTessMemberResultCallback_1_2(obj, member, p1); +} +#endif + +template +class _TessMemberResultCallback_1_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (T::*MemberSignature)(P1,A1,A2) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _TessMemberResultCallback_1_2( T* object, MemberSignature member, P1 p1) + : object_(object), + member_(member), p1_(p1) { } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (object_->*member_)(p1_,a1,a2); + return result; + } else { + R result = (object_->*member_)(p1_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_1_2 : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (T::*MemberSignature)(P1,A1,A2) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _TessMemberResultCallback_1_2( T* object, MemberSignature member, P1 p1) + : object_(object), + member_(member), p1_(p1) { } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (object_->*member_)(p1_,a1,a2); + } else { + (object_->*member_)(p1_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_1_2::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,A1,A2) , typename Identity::type p1) { + return new _TessMemberResultCallback_1_2(obj, member, p1); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_1_2::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,A1,A2) , typename Identity::type p1) { + return new _TessMemberResultCallback_1_2(obj, member, p1); +} +#endif + +template +class _TessFunctionResultCallback_1_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (*FunctionSignature)(P1,A1,A2); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + + public: + inline _TessFunctionResultCallback_1_2(FunctionSignature function, P1 p1) + : function_(function), p1_(p1) { } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (*function_)(p1_,a1,a2); + return result; + } else { + R result = (*function_)(p1_,a1,a2); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_1_2 : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (*FunctionSignature)(P1,A1,A2); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + + public: + inline _TessFunctionResultCallback_1_2(FunctionSignature function, P1 p1) + : function_(function), p1_(p1) { } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (*function_)(p1_,a1,a2); + } else { + (*function_)(p1_,a1,a2); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_1_2::base* +NewTessCallback(R (*function)(P1,A1,A2), typename Identity::type p1) { + return new _TessFunctionResultCallback_1_2(function, p1); +} + +template +inline typename _TessFunctionResultCallback_1_2::base* +NewPermanentTessCallback(R (*function)(P1,A1,A2), typename Identity::type p1) { + return new _TessFunctionResultCallback_1_2(function, p1); +} + +template +class _ConstTessMemberResultCallback_2_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (T::*MemberSignature)(P1,P2,A1,A2) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _ConstTessMemberResultCallback_2_2(const T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,a1,a2); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_2_2 : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (T::*MemberSignature)(P1,P2,A1,A2) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _ConstTessMemberResultCallback_2_2(const T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (object_->*member_)(p1_,p2_,a1,a2); + } else { + (object_->*member_)(p1_,p2_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_2_2::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,A1,A2) const, typename Identity::type p1, typename Identity::type p2) { + return new _ConstTessMemberResultCallback_2_2(obj, member, p1, p2); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_2_2::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,A1,A2) const, typename Identity::type p1, typename Identity::type p2) { + return new _ConstTessMemberResultCallback_2_2(obj, member, p1, p2); +} +#endif + +template +class _TessMemberResultCallback_2_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (T::*MemberSignature)(P1,P2,A1,A2) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessMemberResultCallback_2_2( T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,a1,a2); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_2_2 : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (T::*MemberSignature)(P1,P2,A1,A2) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessMemberResultCallback_2_2( T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (object_->*member_)(p1_,p2_,a1,a2); + } else { + (object_->*member_)(p1_,p2_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_2_2::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,A1,A2) , typename Identity::type p1, typename Identity::type p2) { + return new _TessMemberResultCallback_2_2(obj, member, p1, p2); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_2_2::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,A1,A2) , typename Identity::type p1, typename Identity::type p2) { + return new _TessMemberResultCallback_2_2(obj, member, p1, p2); +} +#endif + +template +class _TessFunctionResultCallback_2_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (*FunctionSignature)(P1,P2,A1,A2); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessFunctionResultCallback_2_2(FunctionSignature function, P1 p1, P2 p2) + : function_(function), p1_(p1), p2_(p2) { } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (*function_)(p1_,p2_,a1,a2); + return result; + } else { + R result = (*function_)(p1_,p2_,a1,a2); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_2_2 : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (*FunctionSignature)(P1,P2,A1,A2); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessFunctionResultCallback_2_2(FunctionSignature function, P1 p1, P2 p2) + : function_(function), p1_(p1), p2_(p2) { } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (*function_)(p1_,p2_,a1,a2); + } else { + (*function_)(p1_,p2_,a1,a2); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_2_2::base* +NewTessCallback(R (*function)(P1,P2,A1,A2), typename Identity::type p1, typename Identity::type p2) { + return new _TessFunctionResultCallback_2_2(function, p1, p2); +} + +template +inline typename _TessFunctionResultCallback_2_2::base* +NewPermanentTessCallback(R (*function)(P1,P2,A1,A2), typename Identity::type p1, typename Identity::type p2) { + return new _TessFunctionResultCallback_2_2(function, p1, p2); +} + +template +class _ConstTessMemberResultCallback_3_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (T::*MemberSignature)(P1,P2,P3,A1,A2) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _ConstTessMemberResultCallback_3_2(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,a1,a2); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_3_2 : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (T::*MemberSignature)(P1,P2,P3,A1,A2) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _ConstTessMemberResultCallback_3_2(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,a1,a2); + } else { + (object_->*member_)(p1_,p2_,p3_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_3_2::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,A1,A2) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _ConstTessMemberResultCallback_3_2(obj, member, p1, p2, p3); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_3_2::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,A1,A2) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _ConstTessMemberResultCallback_3_2(obj, member, p1, p2, p3); +} +#endif + +template +class _TessMemberResultCallback_3_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (T::*MemberSignature)(P1,P2,P3,A1,A2) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessMemberResultCallback_3_2( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,a1,a2); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_3_2 : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (T::*MemberSignature)(P1,P2,P3,A1,A2) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessMemberResultCallback_3_2( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,a1,a2); + } else { + (object_->*member_)(p1_,p2_,p3_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_3_2::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,A1,A2) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessMemberResultCallback_3_2(obj, member, p1, p2, p3); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_3_2::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,A1,A2) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessMemberResultCallback_3_2(obj, member, p1, p2, p3); +} +#endif + +template +class _TessFunctionResultCallback_3_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (*FunctionSignature)(P1,P2,P3,A1,A2); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessFunctionResultCallback_3_2(FunctionSignature function, P1 p1, P2 p2, P3 p3) + : function_(function), p1_(p1), p2_(p2), p3_(p3) { } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,a1,a2); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,a1,a2); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_3_2 : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (*FunctionSignature)(P1,P2,P3,A1,A2); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessFunctionResultCallback_3_2(FunctionSignature function, P1 p1, P2 p2, P3 p3) + : function_(function), p1_(p1), p2_(p2), p3_(p3) { } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (*function_)(p1_,p2_,p3_,a1,a2); + } else { + (*function_)(p1_,p2_,p3_,a1,a2); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_3_2::base* +NewTessCallback(R (*function)(P1,P2,P3,A1,A2), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessFunctionResultCallback_3_2(function, p1, p2, p3); +} + +template +inline typename _TessFunctionResultCallback_3_2::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,A1,A2), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessFunctionResultCallback_3_2(function, p1, p2, p3); +} + +template +class _ConstTessMemberResultCallback_4_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,A1,A2) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _ConstTessMemberResultCallback_4_2(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_4_2 : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,A1,A2) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _ConstTessMemberResultCallback_4_2(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_4_2::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,A1,A2) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _ConstTessMemberResultCallback_4_2(obj, member, p1, p2, p3, p4); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_4_2::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,A1,A2) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _ConstTessMemberResultCallback_4_2(obj, member, p1, p2, p3, p4); +} +#endif + +template +class _TessMemberResultCallback_4_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,A1,A2) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessMemberResultCallback_4_2( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_4_2 : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,A1,A2) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessMemberResultCallback_4_2( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_4_2::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,A1,A2) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessMemberResultCallback_4_2(obj, member, p1, p2, p3, p4); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_4_2::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,A1,A2) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessMemberResultCallback_4_2(obj, member, p1, p2, p3, p4); +} +#endif + +template +class _TessFunctionResultCallback_4_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (*FunctionSignature)(P1,P2,P3,P4,A1,A2); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessFunctionResultCallback_4_2(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,p4_,a1,a2); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,p4_,a1,a2); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_4_2 : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (*FunctionSignature)(P1,P2,P3,P4,A1,A2); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessFunctionResultCallback_4_2(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (*function_)(p1_,p2_,p3_,p4_,a1,a2); + } else { + (*function_)(p1_,p2_,p3_,p4_,a1,a2); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_4_2::base* +NewTessCallback(R (*function)(P1,P2,P3,P4,A1,A2), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessFunctionResultCallback_4_2(function, p1, p2, p3, p4); +} + +template +inline typename _TessFunctionResultCallback_4_2::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,P4,A1,A2), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessFunctionResultCallback_4_2(function, p1, p2, p3, p4); +} + +template +class _ConstTessMemberResultCallback_5_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,A1,A2) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _ConstTessMemberResultCallback_5_2(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_5_2 : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,A1,A2) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _ConstTessMemberResultCallback_5_2(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_5_2::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,A1,A2) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _ConstTessMemberResultCallback_5_2(obj, member, p1, p2, p3, p4, p5); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_5_2::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,A1,A2) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _ConstTessMemberResultCallback_5_2(obj, member, p1, p2, p3, p4, p5); +} +#endif + +template +class _TessMemberResultCallback_5_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,A1,A2) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessMemberResultCallback_5_2( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_5_2 : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,A1,A2) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessMemberResultCallback_5_2( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_5_2::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,A1,A2) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessMemberResultCallback_5_2(obj, member, p1, p2, p3, p4, p5); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_5_2::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,A1,A2) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessMemberResultCallback_5_2(obj, member, p1, p2, p3, p4, p5); +} +#endif + +template +class _TessFunctionResultCallback_5_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (*FunctionSignature)(P1,P2,P3,P4,P5,A1,A2); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessFunctionResultCallback_5_2(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,a1,a2); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,a1,a2); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_5_2 : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (*FunctionSignature)(P1,P2,P3,P4,P5,A1,A2); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessFunctionResultCallback_5_2(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (*function_)(p1_,p2_,p3_,p4_,p5_,a1,a2); + } else { + (*function_)(p1_,p2_,p3_,p4_,p5_,a1,a2); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_5_2::base* +NewTessCallback(R (*function)(P1,P2,P3,P4,P5,A1,A2), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessFunctionResultCallback_5_2(function, p1, p2, p3, p4, p5); +} + +template +inline typename _TessFunctionResultCallback_5_2::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,P4,P5,A1,A2), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessFunctionResultCallback_5_2(function, p1, p2, p3, p4, p5); +} + +template +class _ConstTessMemberResultCallback_6_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,A1,A2) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _ConstTessMemberResultCallback_6_2(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_6_2 : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,A1,A2) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _ConstTessMemberResultCallback_6_2(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_6_2::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6,A1,A2) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _ConstTessMemberResultCallback_6_2(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_6_2::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6,A1,A2) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _ConstTessMemberResultCallback_6_2(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +template +class _TessMemberResultCallback_6_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,A1,A2) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessMemberResultCallback_6_2( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_6_2 : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,A1,A2) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessMemberResultCallback_6_2( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_6_2::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6,A1,A2) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessMemberResultCallback_6_2(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_6_2::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6,A1,A2) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessMemberResultCallback_6_2(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +template +class _TessFunctionResultCallback_6_2 : public TessResultCallback2 { + public: + typedef TessResultCallback2 base; + typedef R (*FunctionSignature)(P1,P2,P3,P4,P5,P6,A1,A2); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessFunctionResultCallback_6_2(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual R Run(A1 a1,A2 a2) { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_6_2 : public TessCallback2 { + public: + typedef TessCallback2 base; + typedef void (*FunctionSignature)(P1,P2,P3,P4,P5,P6,A1,A2); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessFunctionResultCallback_6_2(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual void Run(A1 a1,A2 a2) { + if (!del) { + (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2); + } else { + (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_6_2::base* +NewTessCallback(R (*function)(P1,P2,P3,P4,P5,P6,A1,A2), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessFunctionResultCallback_6_2(function, p1, p2, p3, p4, p5, p6); +} + +template +inline typename _TessFunctionResultCallback_6_2::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,P4,P5,P6,A1,A2), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessFunctionResultCallback_6_2(function, p1, p2, p3, p4, p5, p6); +} + +template +class _ConstTessMemberResultCallback_0_3 : public TessResultCallback3 { + public: + typedef TessResultCallback3 base; + typedef R (T::*MemberSignature)(A1,A2,A3) const; + + private: + const T* object_; + MemberSignature member_; + + public: + inline _ConstTessMemberResultCallback_0_3( + const T* object, MemberSignature member) + : object_(object), + member_(member) { + } + + virtual R Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + R result = (object_->*member_)(a1,a2,a3); + return result; + } else { + R result = (object_->*member_)(a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_0_3 + : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (T::*MemberSignature)(A1,A2,A3) const; + + private: + const T* object_; + MemberSignature member_; + + public: + inline _ConstTessMemberResultCallback_0_3( + const T* object, MemberSignature member) + : object_(object), + member_(member) { + } + + virtual void Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + (object_->*member_)(a1,a2,a3); + } else { + (object_->*member_)(a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_0_3::base* +NewTessCallback( + const T1* obj, R (T2::*member)(A1,A2,A3) const) { + return new _ConstTessMemberResultCallback_0_3( + obj, member); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_0_3::base* +NewPermanentTessCallback( + const T1* obj, R (T2::*member)(A1,A2,A3) const) { + return new _ConstTessMemberResultCallback_0_3( + obj, member); +} +#endif + +template +class _TessMemberResultCallback_0_3 : public TessResultCallback3 { + public: + typedef TessResultCallback3 base; + typedef R (T::*MemberSignature)(A1,A2,A3) ; + + private: + T* object_; + MemberSignature member_; + + public: + inline _TessMemberResultCallback_0_3( + T* object, MemberSignature member) + : object_(object), + member_(member) { + } + + virtual R Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + R result = (object_->*member_)(a1,a2,a3); + return result; + } else { + R result = (object_->*member_)(a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_0_3 + : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (T::*MemberSignature)(A1,A2,A3) ; + + private: + T* object_; + MemberSignature member_; + + public: + inline _TessMemberResultCallback_0_3( + T* object, MemberSignature member) + : object_(object), + member_(member) { + } + + virtual void Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + (object_->*member_)(a1,a2,a3); + } else { + (object_->*member_)(a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_0_3::base* +NewTessCallback( + T1* obj, R (T2::*member)(A1,A2,A3) ) { + return new _TessMemberResultCallback_0_3( + obj, member); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_0_3::base* +NewPermanentTessCallback( + T1* obj, R (T2::*member)(A1,A2,A3) ) { + return new _TessMemberResultCallback_0_3( + obj, member); +} +#endif + +template +class _TessFunctionResultCallback_0_3 : public TessResultCallback3 { + public: + typedef TessResultCallback3 base; + typedef R (*FunctionSignature)(A1,A2,A3); + + private: + FunctionSignature function_; + + public: + inline _TessFunctionResultCallback_0_3( + FunctionSignature function) + : function_(function) { + } + + virtual R Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + R result = (*function_)(a1,a2,a3); + return result; + } else { + R result = (*function_)(a1,a2,a3); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_0_3 + : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (*FunctionSignature)(A1,A2,A3); + + private: + FunctionSignature function_; + + public: + inline _TessFunctionResultCallback_0_3( + FunctionSignature function) + : function_(function) { + } + + virtual void Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + (*function_)(a1,a2,a3); + } else { + (*function_)(a1,a2,a3); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_0_3::base* +NewTessCallback(R (*function)(A1,A2,A3)) { + return new _TessFunctionResultCallback_0_3(function); +} + +template +inline typename _TessFunctionResultCallback_0_3::base* +NewPermanentTessCallback(R (*function)(A1,A2,A3)) { + return new _TessFunctionResultCallback_0_3(function); +} + +template +class _ConstTessMemberResultCallback_1_3 + : public TessResultCallback3 { + public: + typedef TessResultCallback3 base; + typedef R (T::*MemberSignature)(P1,A1,A2,A3) const; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _ConstTessMemberResultCallback_1_3(T* object, + MemberSignature member, P1 p1) + : object_(object), member_(member), p1_(p1) { } + + virtual R Run(A1 a1, A2 a2, A3 a3) { + if (!del) { + R result = (object_->*member_)(p1_,a1,a2,a3); + return result; + } else { + R result = (object_->*member_)(p1_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_1_3 + : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (T::*MemberSignature)(P1,A1,A2,A3) const; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _ConstTessMemberResultCallback_1_3(T* object, + MemberSignature member, P1 p1) + : object_(object), member_(member), p1_(p1) { } + + virtual void Run(A1 a1, A2 a2, A3 a3) { + if (!del) { + (object_->*member_)(p1_,a1,a2,a3); + } else { + (object_->*member_)(p1_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_1_3::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,A1,A2,A3) , typename Identity::type p1) { + return new _ConstTessMemberResultCallback_1_3(obj, member, p1); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_1_3::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,A1,A2,A3) , typename Identity::type p1) { + return new _ConstTessMemberResultCallback_1_3(obj, member, p1); +} +#endif + +template +class _TessMemberResultCallback_1_3 : public TessResultCallback3 { + public: + typedef TessResultCallback3 base; + typedef R (T::*MemberSignature)(P1,A1,A2,A3) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _TessMemberResultCallback_1_3(T* object, + MemberSignature member, P1 p1) + : object_(object), member_(member), p1_(p1) { } + + virtual R Run(A1 a1, A2 a2, A3 a3) { + if (!del) { + R result = (object_->*member_)(p1_,a1,a2,a3); + return result; + } else { + R result = (object_->*member_)(p1_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_1_3 + : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (T::*MemberSignature)(P1,A1,A2,A3) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _TessMemberResultCallback_1_3(T* object, + MemberSignature member, P1 p1) + : object_(object), member_(member), p1_(p1) { } + + virtual void Run(A1 a1, A2 a2, A3 a3) { + if (!del) { + (object_->*member_)(p1_,a1,a2,a3); + } else { + (object_->*member_)(p1_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_1_3::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,A1,A2,A3) , typename Identity::type p1) { + return new _TessMemberResultCallback_1_3(obj, member, p1); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_1_3::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,A1,A2,A3) , typename Identity::type p1) { + return new _TessMemberResultCallback_1_3(obj, member, p1); +} +#endif + +template +class _TessFunctionResultCallback_1_3 : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef R (*FunctionSignature)(P1,A1,A2,A3); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + + public: + inline _TessFunctionResultCallback_1_3(FunctionSignature function, P1 p1) + : function_(function), p1_(p1) { } + + virtual R Run(A1 a1, A2 a2, A3 a3) { + if (!del) { + R result = (*function_)(p1_,a1,a2,a3); + return result; + } else { + R result = (*function_)(p1_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_1_3 + : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (*FunctionSignature)(P1,A1,A2,A3); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + + public: + inline _TessFunctionResultCallback_1_3(FunctionSignature function, P1 p1) + : function_(function), p1_(p1) { } + + virtual void Run(A1 a1, A2 a2, A3 a3) { + if (!del) { + (*function_)(p1_,a1,a2,a3); + } else { + (*function_)(p1_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_1_3::base* +NewTessCallback(R (*function)(P1,A1,A2,A3), typename Identity::type p1) { + return new _TessFunctionResultCallback_1_3(function, p1); +} + +template +inline typename _TessFunctionResultCallback_1_3::base* +NewPermanentTessCallback(R (*function)(P1,A1,A2,A3), typename Identity::type p1) { + return new _TessFunctionResultCallback_1_3(function, p1); +} + +template +class _ConstTessMemberResultCallback_2_3 : public TessResultCallback3 { + public: + typedef TessResultCallback3 base; + typedef R (T::*MemberSignature)(P1,P2,A1,A2,A3) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _ConstTessMemberResultCallback_2_3(const T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual R Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,a1,a2,a3); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_2_3 : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (T::*MemberSignature)(P1,P2,A1,A2,A3) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _ConstTessMemberResultCallback_2_3(const T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual void Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + (object_->*member_)(p1_,p2_,a1,a2,a3); + } else { + (object_->*member_)(p1_,p2_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_2_3::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,A1,A2,A3) const, typename Identity::type p1, typename Identity::type p2) { + return new _ConstTessMemberResultCallback_2_3(obj, member, p1, p2); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_2_3::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,A1,A2,A3) const, typename Identity::type p1, typename Identity::type p2) { + return new _ConstTessMemberResultCallback_2_3(obj, member, p1, p2); +} +#endif + +template +class _TessMemberResultCallback_2_3 : public TessResultCallback3 { + public: + typedef TessResultCallback3 base; + typedef R (T::*MemberSignature)(P1,P2,A1,A2,A3) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessMemberResultCallback_2_3( T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + + virtual R Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,a1,a2,a3); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_2_3 : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (T::*MemberSignature)(P1,P2,A1,A2,A3) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessMemberResultCallback_2_3( T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual void Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + (object_->*member_)(p1_,p2_,a1,a2,a3); + } else { + (object_->*member_)(p1_,p2_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_2_3::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,A1,A2,A3) , typename Identity::type p1, typename Identity::type p2) { + return new _TessMemberResultCallback_2_3(obj, member, p1, p2); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_2_3::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,A1,A2,A3) , typename Identity::type p1, typename Identity::type p2) { + return new _TessMemberResultCallback_2_3(obj, member, p1, p2); +} +#endif + +template +class _TessFunctionResultCallback_2_3 : public TessResultCallback3 { + public: + typedef TessResultCallback3 base; + typedef R (*FunctionSignature)(P1,P2,A1,A2,A3); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessFunctionResultCallback_2_3(FunctionSignature function, P1 p1, P2 p2) + : function_(function), p1_(p1), p2_(p2) { } + + virtual R Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + R result = (*function_)(p1_,p2_,a1,a2,a3); + return result; + } else { + R result = (*function_)(p1_,p2_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_2_3 : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (*FunctionSignature)(P1,P2,A1,A2,A3); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessFunctionResultCallback_2_3(FunctionSignature function, P1 p1, P2 p2) + : function_(function), p1_(p1), p2_(p2) { } + + virtual void Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + (*function_)(p1_,p2_,a1,a2,a3); + } else { + (*function_)(p1_,p2_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_2_3::base* +NewTessCallback(R (*function)(P1,P2,A1,A2,A3), typename Identity::type p1, typename Identity::type p2) { + return new _TessFunctionResultCallback_2_3(function, p1, p2); +} + +template +inline typename _TessFunctionResultCallback_2_3::base* +NewPermanentTessCallback(R (*function)(P1,P2,A1,A2,A3), typename Identity::type p1, typename Identity::type p2) { + return new _TessFunctionResultCallback_2_3(function, p1, p2); +} + +template +class _ConstTessMemberResultCallback_3_3 : public TessResultCallback3 { + public: + typedef TessResultCallback3 base; + typedef R (T::*MemberSignature)(P1,P2,P3,A1,A2,A3) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _ConstTessMemberResultCallback_3_3(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual R Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,a1,a2,a3); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_3_3 : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (T::*MemberSignature)(P1,P2,P3,A1,A2,A3) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _ConstTessMemberResultCallback_3_3(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual void Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,a1,a2,a3); + } else { + (object_->*member_)(p1_,p2_,p3_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_3_3::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,A1,A2,A3) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _ConstTessMemberResultCallback_3_3(obj, member, p1, p2, p3); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_3_3::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,A1,A2,A3) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _ConstTessMemberResultCallback_3_3(obj, member, p1, p2, p3); +} +#endif + +template +class _TessMemberResultCallback_3_3 : public TessResultCallback3 { + public: + typedef TessResultCallback3 base; + typedef R (T::*MemberSignature)(P1,P2,P3,A1,A2,A3) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessMemberResultCallback_3_3( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual R Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,a1,a2,a3); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_3_3 : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (T::*MemberSignature)(P1,P2,P3,A1,A2,A3) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessMemberResultCallback_3_3( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual void Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,a1,a2,a3); + } else { + (object_->*member_)(p1_,p2_,p3_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_3_3::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,A1,A2,A3) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessMemberResultCallback_3_3(obj, member, p1, p2, p3); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_3_3::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,A1,A2,A3) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessMemberResultCallback_3_3(obj, member, p1, p2, p3); +} +#endif + +template +class _TessFunctionResultCallback_3_3 : public TessResultCallback3 { + public: + typedef TessResultCallback3 base; + typedef R (*FunctionSignature)(P1,P2,P3,A1,A2,A3); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessFunctionResultCallback_3_3(FunctionSignature function, P1 p1, P2 p2, P3 p3) + : function_(function), p1_(p1), p2_(p2), p3_(p3) { } + + virtual R Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,a1,a2,a3); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_3_3 : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (*FunctionSignature)(P1,P2,P3,A1,A2,A3); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessFunctionResultCallback_3_3(FunctionSignature function, P1 p1, P2 p2, P3 p3) + : function_(function), p1_(p1), p2_(p2), p3_(p3) { } + + virtual void Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + (*function_)(p1_,p2_,p3_,a1,a2,a3); + } else { + (*function_)(p1_,p2_,p3_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_3_3::base* +NewTessCallback(R (*function)(P1,P2,P3,A1,A2,A3), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessFunctionResultCallback_3_3(function, p1, p2, p3); +} + +template +inline typename _TessFunctionResultCallback_3_3::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,A1,A2,A3), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessFunctionResultCallback_3_3(function, p1, p2, p3); +} + +template +class _ConstTessMemberResultCallback_4_3 : public TessResultCallback3 { + public: + typedef TessResultCallback3 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,A1,A2,A3) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _ConstTessMemberResultCallback_4_3(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual R Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_4_3 : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,A1,A2,A3) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _ConstTessMemberResultCallback_4_3(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual void Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_4_3::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,A1,A2,A3) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _ConstTessMemberResultCallback_4_3(obj, member, p1, p2, p3, p4); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_4_3::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,A1,A2,A3) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _ConstTessMemberResultCallback_4_3(obj, member, p1, p2, p3, p4); +} +#endif + +template +class _TessMemberResultCallback_4_3 : public TessResultCallback3 { + public: + typedef TessResultCallback3 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,A1,A2,A3) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessMemberResultCallback_4_3( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual R Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_4_3 : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,A1,A2,A3) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessMemberResultCallback_4_3( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual void Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_4_3::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,A1,A2,A3) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessMemberResultCallback_4_3(obj, member, p1, p2, p3, p4); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_4_3::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,A1,A2,A3) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessMemberResultCallback_4_3(obj, member, p1, p2, p3, p4); +} +#endif + +template +class _TessFunctionResultCallback_4_3 : public TessResultCallback3 { + public: + typedef TessResultCallback3 base; + typedef R (*FunctionSignature)(P1,P2,P3,P4,A1,A2,A3); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessFunctionResultCallback_4_3(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual R Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,p4_,a1,a2,a3); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,p4_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_4_3 : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (*FunctionSignature)(P1,P2,P3,P4,A1,A2,A3); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessFunctionResultCallback_4_3(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual void Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + (*function_)(p1_,p2_,p3_,p4_,a1,a2,a3); + } else { + (*function_)(p1_,p2_,p3_,p4_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_4_3::base* +NewTessCallback(R (*function)(P1,P2,P3,P4,A1,A2,A3), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessFunctionResultCallback_4_3(function, p1, p2, p3, p4); +} + +template +inline typename _TessFunctionResultCallback_4_3::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,P4,A1,A2,A3), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessFunctionResultCallback_4_3(function, p1, p2, p3, p4); +} + +template +class _ConstTessMemberResultCallback_5_3 : public TessResultCallback3 { + public: + typedef TessResultCallback3 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,A1,A2,A3) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _ConstTessMemberResultCallback_5_3(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual R Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_5_3 : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,A1,A2,A3) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _ConstTessMemberResultCallback_5_3(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual void Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_5_3::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,A1,A2,A3) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _ConstTessMemberResultCallback_5_3(obj, member, p1, p2, p3, p4, p5); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_5_3::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,A1,A2,A3) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _ConstTessMemberResultCallback_5_3(obj, member, p1, p2, p3, p4, p5); +} +#endif + +template +class _TessMemberResultCallback_5_3 : public TessResultCallback3 { + public: + typedef TessResultCallback3 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,A1,A2,A3) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessMemberResultCallback_5_3( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual R Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_5_3 : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,A1,A2,A3) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessMemberResultCallback_5_3( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual void Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_5_3::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,A1,A2,A3) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessMemberResultCallback_5_3(obj, member, p1, p2, p3, p4, p5); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_5_3::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,A1,A2,A3) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessMemberResultCallback_5_3(obj, member, p1, p2, p3, p4, p5); +} +#endif + +template +class _TessFunctionResultCallback_5_3 : public TessResultCallback3 { + public: + typedef TessResultCallback3 base; + typedef R (*FunctionSignature)(P1,P2,P3,P4,P5,A1,A2,A3); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessFunctionResultCallback_5_3(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual R Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_5_3 : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (*FunctionSignature)(P1,P2,P3,P4,P5,A1,A2,A3); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessFunctionResultCallback_5_3(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual void Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + (*function_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3); + } else { + (*function_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_5_3::base* +NewTessCallback(R (*function)(P1,P2,P3,P4,P5,A1,A2,A3), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessFunctionResultCallback_5_3(function, p1, p2, p3, p4, p5); +} + +template +inline typename _TessFunctionResultCallback_5_3::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,P4,P5,A1,A2,A3), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessFunctionResultCallback_5_3(function, p1, p2, p3, p4, p5); +} + +template +class _ConstTessMemberResultCallback_6_3 : public TessResultCallback3 { + public: + typedef TessResultCallback3 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,A1,A2,A3) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _ConstTessMemberResultCallback_6_3(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual R Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_6_3 : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,A1,A2,A3) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _ConstTessMemberResultCallback_6_3(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual void Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_6_3::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6,A1,A2,A3) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _ConstTessMemberResultCallback_6_3(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_6_3::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6,A1,A2,A3) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _ConstTessMemberResultCallback_6_3(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +template +class _TessMemberResultCallback_6_3 : public TessResultCallback3 { + public: + typedef TessResultCallback3 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,A1,A2,A3) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessMemberResultCallback_6_3( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual R Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_6_3 : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,A1,A2,A3) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessMemberResultCallback_6_3( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual void Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_6_3::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6,A1,A2,A3) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessMemberResultCallback_6_3(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_6_3::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6,A1,A2,A3) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessMemberResultCallback_6_3(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +template +class _TessFunctionResultCallback_6_3 : public TessResultCallback3 { + public: + typedef TessResultCallback3 base; + typedef R (*FunctionSignature)(P1,P2,P3,P4,P5,P6,A1,A2,A3); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessFunctionResultCallback_6_3(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual R Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_6_3 : public TessCallback3 { + public: + typedef TessCallback3 base; + typedef void (*FunctionSignature)(P1,P2,P3,P4,P5,P6,A1,A2,A3); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessFunctionResultCallback_6_3(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual void Run(A1 a1,A2 a2,A3 a3) { + if (!del) { + (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3); + } else { + (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_6_3::base* +NewTessCallback(R (*function)(P1,P2,P3,P4,P5,P6,A1,A2,A3), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessFunctionResultCallback_6_3(function, p1, p2, p3, p4, p5, p6); +} + +template +inline typename _TessFunctionResultCallback_6_3::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,P4,P5,P6,A1,A2,A3), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessFunctionResultCallback_6_3(function, p1, p2, p3, p4, p5, p6); +} + +template +class _ConstTessMemberResultCallback_0_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (T::*MemberSignature)(A1,A2,A3,A4) const; + + private: + const T* object_; + MemberSignature member_; + + public: + inline _ConstTessMemberResultCallback_0_4(const T* object, MemberSignature member) + : object_(object), + member_(member) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (object_->*member_)(a1,a2,a3,a4); + return result; + } else { + R result = (object_->*member_)(a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_0_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (T::*MemberSignature)(A1,A2,A3,A4) const; + + private: + const T* object_; + MemberSignature member_; + + public: + inline _ConstTessMemberResultCallback_0_4(const T* object, MemberSignature member) + : object_(object), + member_(member) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (object_->*member_)(a1,a2,a3,a4); + } else { + (object_->*member_)(a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_0_4::base* +NewTessCallback(const T1* obj, R (T2::*member)(A1,A2,A3,A4) const) { + return new _ConstTessMemberResultCallback_0_4(obj, member); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_0_4::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(A1,A2,A3,A4) const) { + return new _ConstTessMemberResultCallback_0_4(obj, member); +} +#endif + +template +class _TessMemberResultCallback_0_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (T::*MemberSignature)(A1,A2,A3,A4) ; + + private: + T* object_; + MemberSignature member_; + + public: + inline _TessMemberResultCallback_0_4( T* object, MemberSignature member) + : object_(object), + member_(member) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (object_->*member_)(a1,a2,a3,a4); + return result; + } else { + R result = (object_->*member_)(a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_0_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (T::*MemberSignature)(A1,A2,A3,A4) ; + + private: + T* object_; + MemberSignature member_; + + public: + inline _TessMemberResultCallback_0_4( T* object, MemberSignature member) + : object_(object), + member_(member) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (object_->*member_)(a1,a2,a3,a4); + } else { + (object_->*member_)(a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_0_4::base* +NewTessCallback( T1* obj, R (T2::*member)(A1,A2,A3,A4) ) { + return new _TessMemberResultCallback_0_4(obj, member); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_0_4::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(A1,A2,A3,A4) ) { + return new _TessMemberResultCallback_0_4(obj, member); +} +#endif + +template +class _TessFunctionResultCallback_0_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (*FunctionSignature)(A1,A2,A3,A4); + + private: + FunctionSignature function_; + + public: + inline _TessFunctionResultCallback_0_4(FunctionSignature function) + : function_(function) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (*function_)(a1,a2,a3,a4); + return result; + } else { + R result = (*function_)(a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_0_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (*FunctionSignature)(A1,A2,A3,A4); + + private: + FunctionSignature function_; + + public: + inline _TessFunctionResultCallback_0_4(FunctionSignature function) + : function_(function) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (*function_)(a1,a2,a3,a4); + } else { + (*function_)(a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_0_4::base* +NewTessCallback(R (*function)(A1,A2,A3,A4)) { + return new _TessFunctionResultCallback_0_4(function); +} + +template +inline typename _TessFunctionResultCallback_0_4::base* +NewPermanentTessCallback(R (*function)(A1,A2,A3,A4)) { + return new _TessFunctionResultCallback_0_4(function); +} + +template +class _ConstTessMemberResultCallback_1_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (T::*MemberSignature)(P1,A1,A2,A3,A4) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _ConstTessMemberResultCallback_1_4(const T* object, MemberSignature member, P1 p1) + : object_(object), + member_(member), p1_(p1) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (object_->*member_)(p1_,a1,a2,a3,a4); + return result; + } else { + R result = (object_->*member_)(p1_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_1_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (T::*MemberSignature)(P1,A1,A2,A3,A4) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _ConstTessMemberResultCallback_1_4(const T* object, MemberSignature member, P1 p1) + : object_(object), + member_(member), p1_(p1) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (object_->*member_)(p1_,a1,a2,a3,a4); + } else { + (object_->*member_)(p1_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_1_4::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,A1,A2,A3,A4) const, typename Identity::type p1) { + return new _ConstTessMemberResultCallback_1_4(obj, member, p1); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_1_4::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,A1,A2,A3,A4) const, typename Identity::type p1) { + return new _ConstTessMemberResultCallback_1_4(obj, member, p1); +} +#endif + +template +class _TessMemberResultCallback_1_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (T::*MemberSignature)(P1,A1,A2,A3,A4) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _TessMemberResultCallback_1_4( T* object, MemberSignature member, P1 p1) + : object_(object), + member_(member), p1_(p1) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (object_->*member_)(p1_,a1,a2,a3,a4); + return result; + } else { + R result = (object_->*member_)(p1_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_1_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (T::*MemberSignature)(P1,A1,A2,A3,A4) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _TessMemberResultCallback_1_4( T* object, MemberSignature member, P1 p1) + : object_(object), + member_(member), p1_(p1) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (object_->*member_)(p1_,a1,a2,a3,a4); + } else { + (object_->*member_)(p1_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_1_4::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,A1,A2,A3,A4) , typename Identity::type p1) { + return new _TessMemberResultCallback_1_4(obj, member, p1); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_1_4::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,A1,A2,A3,A4) , typename Identity::type p1) { + return new _TessMemberResultCallback_1_4(obj, member, p1); +} +#endif + +template +class _TessFunctionResultCallback_1_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (*FunctionSignature)(P1,A1,A2,A3,A4); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + + public: + inline _TessFunctionResultCallback_1_4(FunctionSignature function, P1 p1) + : function_(function), p1_(p1) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (*function_)(p1_,a1,a2,a3,a4); + return result; + } else { + R result = (*function_)(p1_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_1_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (*FunctionSignature)(P1,A1,A2,A3,A4); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + + public: + inline _TessFunctionResultCallback_1_4(FunctionSignature function, P1 p1) + : function_(function), p1_(p1) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (*function_)(p1_,a1,a2,a3,a4); + } else { + (*function_)(p1_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_1_4::base* +NewTessCallback(R (*function)(P1,A1,A2,A3,A4), typename Identity::type p1) { + return new _TessFunctionResultCallback_1_4(function, p1); +} + +template +inline typename _TessFunctionResultCallback_1_4::base* +NewPermanentTessCallback(R (*function)(P1,A1,A2,A3,A4), typename Identity::type p1) { + return new _TessFunctionResultCallback_1_4(function, p1); +} + +template +class _ConstTessMemberResultCallback_2_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (T::*MemberSignature)(P1,P2,A1,A2,A3,A4) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _ConstTessMemberResultCallback_2_4(const T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,a1,a2,a3,a4); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_2_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (T::*MemberSignature)(P1,P2,A1,A2,A3,A4) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _ConstTessMemberResultCallback_2_4(const T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (object_->*member_)(p1_,p2_,a1,a2,a3,a4); + } else { + (object_->*member_)(p1_,p2_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_2_4::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,A1,A2,A3,A4) const, typename Identity::type p1, typename Identity::type p2) { + return new _ConstTessMemberResultCallback_2_4(obj, member, p1, p2); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_2_4::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,A1,A2,A3,A4) const, typename Identity::type p1, typename Identity::type p2) { + return new _ConstTessMemberResultCallback_2_4(obj, member, p1, p2); +} +#endif + +template +class _TessMemberResultCallback_2_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (T::*MemberSignature)(P1,P2,A1,A2,A3,A4) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessMemberResultCallback_2_4( T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,a1,a2,a3,a4); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_2_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (T::*MemberSignature)(P1,P2,A1,A2,A3,A4) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessMemberResultCallback_2_4( T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (object_->*member_)(p1_,p2_,a1,a2,a3,a4); + } else { + (object_->*member_)(p1_,p2_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_2_4::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,A1,A2,A3,A4) , typename Identity::type p1, typename Identity::type p2) { + return new _TessMemberResultCallback_2_4(obj, member, p1, p2); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_2_4::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,A1,A2,A3,A4) , typename Identity::type p1, typename Identity::type p2) { + return new _TessMemberResultCallback_2_4(obj, member, p1, p2); +} +#endif + +template +class _TessFunctionResultCallback_2_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (*FunctionSignature)(P1,P2,A1,A2,A3,A4); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessFunctionResultCallback_2_4(FunctionSignature function, P1 p1, P2 p2) + : function_(function), p1_(p1), p2_(p2) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (*function_)(p1_,p2_,a1,a2,a3,a4); + return result; + } else { + R result = (*function_)(p1_,p2_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_2_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (*FunctionSignature)(P1,P2,A1,A2,A3,A4); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessFunctionResultCallback_2_4(FunctionSignature function, P1 p1, P2 p2) + : function_(function), p1_(p1), p2_(p2) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (*function_)(p1_,p2_,a1,a2,a3,a4); + } else { + (*function_)(p1_,p2_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_2_4::base* +NewTessCallback(R (*function)(P1,P2,A1,A2,A3,A4), typename Identity::type p1, typename Identity::type p2) { + return new _TessFunctionResultCallback_2_4(function, p1, p2); +} + +template +inline typename _TessFunctionResultCallback_2_4::base* +NewPermanentTessCallback(R (*function)(P1,P2,A1,A2,A3,A4), typename Identity::type p1, typename Identity::type p2) { + return new _TessFunctionResultCallback_2_4(function, p1, p2); +} + +template +class _ConstTessMemberResultCallback_3_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (T::*MemberSignature)(P1,P2,P3,A1,A2,A3,A4) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _ConstTessMemberResultCallback_3_4(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,a1,a2,a3,a4); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_3_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (T::*MemberSignature)(P1,P2,P3,A1,A2,A3,A4) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _ConstTessMemberResultCallback_3_4(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,a1,a2,a3,a4); + } else { + (object_->*member_)(p1_,p2_,p3_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_3_4::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,A1,A2,A3,A4) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _ConstTessMemberResultCallback_3_4(obj, member, p1, p2, p3); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_3_4::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,A1,A2,A3,A4) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _ConstTessMemberResultCallback_3_4(obj, member, p1, p2, p3); +} +#endif + +template +class _TessMemberResultCallback_3_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (T::*MemberSignature)(P1,P2,P3,A1,A2,A3,A4) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessMemberResultCallback_3_4( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,a1,a2,a3,a4); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_3_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (T::*MemberSignature)(P1,P2,P3,A1,A2,A3,A4) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessMemberResultCallback_3_4( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,a1,a2,a3,a4); + } else { + (object_->*member_)(p1_,p2_,p3_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_3_4::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,A1,A2,A3,A4) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessMemberResultCallback_3_4(obj, member, p1, p2, p3); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_3_4::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,A1,A2,A3,A4) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessMemberResultCallback_3_4(obj, member, p1, p2, p3); +} +#endif + +template +class _TessFunctionResultCallback_3_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (*FunctionSignature)(P1,P2,P3,A1,A2,A3,A4); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessFunctionResultCallback_3_4(FunctionSignature function, P1 p1, P2 p2, P3 p3) + : function_(function), p1_(p1), p2_(p2), p3_(p3) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,a1,a2,a3,a4); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_3_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (*FunctionSignature)(P1,P2,P3,A1,A2,A3,A4); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + + public: + inline _TessFunctionResultCallback_3_4(FunctionSignature function, P1 p1, P2 p2, P3 p3) + : function_(function), p1_(p1), p2_(p2), p3_(p3) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (*function_)(p1_,p2_,p3_,a1,a2,a3,a4); + } else { + (*function_)(p1_,p2_,p3_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_3_4::base* +NewTessCallback(R (*function)(P1,P2,P3,A1,A2,A3,A4), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessFunctionResultCallback_3_4(function, p1, p2, p3); +} + +template +inline typename _TessFunctionResultCallback_3_4::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,A1,A2,A3,A4), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessFunctionResultCallback_3_4(function, p1, p2, p3); +} + +template +class _ConstTessMemberResultCallback_4_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,A1,A2,A3,A4) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _ConstTessMemberResultCallback_4_4(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_4_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,A1,A2,A3,A4) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _ConstTessMemberResultCallback_4_4(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_4_4::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,A1,A2,A3,A4) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _ConstTessMemberResultCallback_4_4(obj, member, p1, p2, p3, p4); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_4_4::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,A1,A2,A3,A4) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _ConstTessMemberResultCallback_4_4(obj, member, p1, p2, p3, p4); +} +#endif + +template +class _TessMemberResultCallback_4_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,A1,A2,A3,A4) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessMemberResultCallback_4_4( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_4_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,A1,A2,A3,A4) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessMemberResultCallback_4_4( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_4_4::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,A1,A2,A3,A4) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessMemberResultCallback_4_4(obj, member, p1, p2, p3, p4); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_4_4::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,A1,A2,A3,A4) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessMemberResultCallback_4_4(obj, member, p1, p2, p3, p4); +} +#endif + +template +class _TessFunctionResultCallback_4_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (*FunctionSignature)(P1,P2,P3,P4,A1,A2,A3,A4); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessFunctionResultCallback_4_4(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_4_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (*FunctionSignature)(P1,P2,P3,P4,A1,A2,A3,A4); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + + public: + inline _TessFunctionResultCallback_4_4(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (*function_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4); + } else { + (*function_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_4_4::base* +NewTessCallback(R (*function)(P1,P2,P3,P4,A1,A2,A3,A4), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessFunctionResultCallback_4_4(function, p1, p2, p3, p4); +} + +template +inline typename _TessFunctionResultCallback_4_4::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,P4,A1,A2,A3,A4), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessFunctionResultCallback_4_4(function, p1, p2, p3, p4); +} + +template +class _ConstTessMemberResultCallback_5_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,A1,A2,A3,A4) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _ConstTessMemberResultCallback_5_4(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_5_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,A1,A2,A3,A4) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _ConstTessMemberResultCallback_5_4(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_5_4::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,A1,A2,A3,A4) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _ConstTessMemberResultCallback_5_4(obj, member, p1, p2, p3, p4, p5); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_5_4::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,A1,A2,A3,A4) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _ConstTessMemberResultCallback_5_4(obj, member, p1, p2, p3, p4, p5); +} +#endif + +template +class _TessMemberResultCallback_5_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,A1,A2,A3,A4) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessMemberResultCallback_5_4( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_5_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,A1,A2,A3,A4) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessMemberResultCallback_5_4( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_5_4::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,A1,A2,A3,A4) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessMemberResultCallback_5_4(obj, member, p1, p2, p3, p4, p5); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_5_4::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,A1,A2,A3,A4) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessMemberResultCallback_5_4(obj, member, p1, p2, p3, p4, p5); +} +#endif + +template +class _TessFunctionResultCallback_5_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (*FunctionSignature)(P1,P2,P3,P4,P5,A1,A2,A3,A4); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessFunctionResultCallback_5_4(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_5_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (*FunctionSignature)(P1,P2,P3,P4,P5,A1,A2,A3,A4); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + + public: + inline _TessFunctionResultCallback_5_4(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (*function_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4); + } else { + (*function_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_5_4::base* +NewTessCallback(R (*function)(P1,P2,P3,P4,P5,A1,A2,A3,A4), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessFunctionResultCallback_5_4(function, p1, p2, p3, p4, p5); +} + +template +inline typename _TessFunctionResultCallback_5_4::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,P4,P5,A1,A2,A3,A4), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessFunctionResultCallback_5_4(function, p1, p2, p3, p4, p5); +} + +template +class _ConstTessMemberResultCallback_6_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _ConstTessMemberResultCallback_6_4(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_6_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _ConstTessMemberResultCallback_6_4(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_6_4::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _ConstTessMemberResultCallback_6_4(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_6_4::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _ConstTessMemberResultCallback_6_4(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +template +class _TessMemberResultCallback_6_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessMemberResultCallback_6_4( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_6_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessMemberResultCallback_6_4( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4); + } else { + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_6_4::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessMemberResultCallback_6_4(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_6_4::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessMemberResultCallback_6_4(obj, member, p1, p2, p3, p4, p5, p6); +} +#endif + +template +class _TessFunctionResultCallback_6_4 : public TessResultCallback4 { + public: + typedef TessResultCallback4 base; + typedef R (*FunctionSignature)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessFunctionResultCallback_6_4(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4); + return result; + } else { + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_6_4 : public TessCallback4 { + public: + typedef TessCallback4 base; + typedef void (*FunctionSignature)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; + + public: + inline _TessFunctionResultCallback_6_4(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4) { + if (!del) { + (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4); + } else { + (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_6_4::base* +NewTessCallback(R (*function)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessFunctionResultCallback_6_4(function, p1, p2, p3, p4, p5, p6); +} + +template +inline typename _TessFunctionResultCallback_6_4::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessFunctionResultCallback_6_4(function, p1, p2, p3, p4, p5, p6); +} + +template +class TessCallback5 { + public: + virtual ~TessCallback5() { } + virtual void Run(A1,A2,A3,A4,A5) = 0; +}; + +template +class TessResultCallback5 { + public: + virtual ~TessResultCallback5() { } + virtual R Run(A1,A2,A3,A4,A5) = 0; +}; + +template +class _ConstTessMemberResultCallback_0_5 : public TessResultCallback5 { + public: + typedef TessResultCallback5 base; + typedef R (T::*MemberSignature)(A1,A2,A3,A4,A5) const; + + private: + const T* object_; + MemberSignature member_; + + public: + inline _ConstTessMemberResultCallback_0_5(const T* object, MemberSignature member) + : object_(object), + member_(member) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + R result = (object_->*member_)(a1,a2,a3,a4,a5); + return result; + } else { + R result = (object_->*member_)(a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_0_5 : public TessCallback5 { + public: + typedef TessCallback5 base; + typedef void (T::*MemberSignature)(A1,A2,A3,A4,A5) const; + + private: + const T* object_; + MemberSignature member_; + + public: + inline _ConstTessMemberResultCallback_0_5(const T* object, MemberSignature member) + : object_(object), + member_(member) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + (object_->*member_)(a1,a2,a3,a4,a5); + } else { + (object_->*member_)(a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_0_5::base* +NewTessCallback(const T1* obj, R (T2::*member)(A1,A2,A3,A4,A5) const) { + return new _ConstTessMemberResultCallback_0_5(obj, member); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_0_5::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(A1,A2,A3,A4,A5) const) { + return new _ConstTessMemberResultCallback_0_5(obj, member); +} +#endif + +template +class _TessMemberResultCallback_0_5 : public TessResultCallback5 { + public: + typedef TessResultCallback5 base; + typedef R (T::*MemberSignature)(A1,A2,A3,A4,A5) ; + + private: + T* object_; + MemberSignature member_; + + public: + inline _TessMemberResultCallback_0_5( T* object, MemberSignature member) + : object_(object), + member_(member) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + R result = (object_->*member_)(a1,a2,a3,a4,a5); + return result; + } else { + R result = (object_->*member_)(a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_0_5 : public TessCallback5 { + public: + typedef TessCallback5 base; + typedef void (T::*MemberSignature)(A1,A2,A3,A4,A5) ; + + private: + T* object_; + MemberSignature member_; + + public: + inline _TessMemberResultCallback_0_5( T* object, MemberSignature member) + : object_(object), + member_(member) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + (object_->*member_)(a1,a2,a3,a4,a5); + } else { + (object_->*member_)(a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_0_5::base* +NewTessCallback( T1* obj, R (T2::*member)(A1,A2,A3,A4,A5) ) { + return new _TessMemberResultCallback_0_5(obj, member); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_0_5::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(A1,A2,A3,A4,A5) ) { + return new _TessMemberResultCallback_0_5(obj, member); +} +#endif + +template +class _TessFunctionResultCallback_0_5 : public TessResultCallback5 { + public: + typedef TessResultCallback5 base; + typedef R (*FunctionSignature)(A1,A2,A3,A4,A5); + + private: + FunctionSignature function_; + + public: + inline _TessFunctionResultCallback_0_5(FunctionSignature function) + : function_(function) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + R result = (*function_)(a1,a2,a3,a4,a5); + return result; + } else { + R result = (*function_)(a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_0_5 : public TessCallback5 { + public: + typedef TessCallback5 base; + typedef void (*FunctionSignature)(A1,A2,A3,A4,A5); + + private: + FunctionSignature function_; + + public: + inline _TessFunctionResultCallback_0_5(FunctionSignature function) + : function_(function) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + (*function_)(a1,a2,a3,a4,a5); + } else { + (*function_)(a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_0_5::base* +NewTessCallback(R (*function)(A1,A2,A3,A4,A5)) { + return new _TessFunctionResultCallback_0_5(function); +} + +template +inline typename _TessFunctionResultCallback_0_5::base* +NewPermanentTessCallback(R (*function)(A1,A2,A3,A4,A5)) { + return new _TessFunctionResultCallback_0_5(function); +} + +template +class _ConstTessMemberResultCallback_1_5 : public TessResultCallback5 { + public: + typedef TessResultCallback5 base; + typedef R (T::*MemberSignature)(P1,A1,A2,A3,A4,A5) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _ConstTessMemberResultCallback_1_5(const T* object, MemberSignature member, P1 p1) + : object_(object), + member_(member), p1_(p1) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + R result = (object_->*member_)(p1_,a1,a2,a3,a4,a5); + return result; + } else { + R result = (object_->*member_)(p1_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_1_5 : public TessCallback5 { + public: + typedef TessCallback5 base; + typedef void (T::*MemberSignature)(P1,A1,A2,A3,A4,A5) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _ConstTessMemberResultCallback_1_5(const T* object, MemberSignature member, P1 p1) + : object_(object), + member_(member), p1_(p1) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + (object_->*member_)(p1_,a1,a2,a3,a4,a5); + } else { + (object_->*member_)(p1_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_1_5::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,A1,A2,A3,A4,A5) const, typename Identity::type p1) { + return new _ConstTessMemberResultCallback_1_5(obj, member, p1); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_1_5::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,A1,A2,A3,A4,A5) const, typename Identity::type p1) { + return new _ConstTessMemberResultCallback_1_5(obj, member, p1); +} +#endif + +template +class _TessMemberResultCallback_1_5 : public TessResultCallback5 { + public: + typedef TessResultCallback5 base; + typedef R (T::*MemberSignature)(P1,A1,A2,A3,A4,A5) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _TessMemberResultCallback_1_5( T* object, MemberSignature member, P1 p1) + : object_(object), + member_(member), p1_(p1) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + R result = (object_->*member_)(p1_,a1,a2,a3,a4,a5); + return result; + } else { + R result = (object_->*member_)(p1_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_1_5 : public TessCallback5 { + public: + typedef TessCallback5 base; + typedef void (T::*MemberSignature)(P1,A1,A2,A3,A4,A5) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + + public: + inline _TessMemberResultCallback_1_5( T* object, MemberSignature member, P1 p1) + : object_(object), + member_(member), p1_(p1) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + (object_->*member_)(p1_,a1,a2,a3,a4,a5); + } else { + (object_->*member_)(p1_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_1_5::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,A1,A2,A3,A4,A5) , typename Identity::type p1) { + return new _TessMemberResultCallback_1_5(obj, member, p1); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_1_5::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,A1,A2,A3,A4,A5) , typename Identity::type p1) { + return new _TessMemberResultCallback_1_5(obj, member, p1); +} +#endif + +template +class _TessFunctionResultCallback_1_5 : public TessResultCallback5 { + public: + typedef TessResultCallback5 base; + typedef R (*FunctionSignature)(P1,A1,A2,A3,A4,A5); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + + public: + inline _TessFunctionResultCallback_1_5(FunctionSignature function, P1 p1) + : function_(function), p1_(p1) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + R result = (*function_)(p1_,a1,a2,a3,a4,a5); + return result; + } else { + R result = (*function_)(p1_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessFunctionResultCallback_1_5 : public TessCallback5 { + public: + typedef TessCallback5 base; + typedef void (*FunctionSignature)(P1,A1,A2,A3,A4,A5); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + + public: + inline _TessFunctionResultCallback_1_5(FunctionSignature function, P1 p1) + : function_(function), p1_(p1) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + (*function_)(p1_,a1,a2,a3,a4,a5); + } else { + (*function_)(p1_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } +}; + +template +inline typename _TessFunctionResultCallback_1_5::base* +NewTessCallback(R (*function)(P1,A1,A2,A3,A4,A5), typename Identity::type p1) { + return new _TessFunctionResultCallback_1_5(function, p1); +} + +template +inline typename _TessFunctionResultCallback_1_5::base* +NewPermanentTessCallback(R (*function)(P1,A1,A2,A3,A4,A5), typename Identity::type p1) { + return new _TessFunctionResultCallback_1_5(function, p1); +} + +template +class _ConstTessMemberResultCallback_2_5 : public TessResultCallback5 { + public: + typedef TessResultCallback5 base; + typedef R (T::*MemberSignature)(P1,P2,A1,A2,A3,A4,A5) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _ConstTessMemberResultCallback_2_5(const T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,a1,a2,a3,a4,a5); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _ConstTessMemberResultCallback_2_5 : public TessCallback5 { + public: + typedef TessCallback5 base; + typedef void (T::*MemberSignature)(P1,P2,A1,A2,A3,A4,A5) const; + + private: + const T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _ConstTessMemberResultCallback_2_5(const T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + (object_->*member_)(p1_,p2_,a1,a2,a3,a4,a5); + } else { + (object_->*member_)(p1_,p2_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_2_5::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,A1,A2,A3,A4,A5) const, typename Identity::type p1, typename Identity::type p2) { + return new _ConstTessMemberResultCallback_2_5(obj, member, p1, p2); +} +#endif + +#ifndef SWIG +template +inline typename _ConstTessMemberResultCallback_2_5::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,A1,A2,A3,A4,A5) const, typename Identity::type p1, typename Identity::type p2) { + return new _ConstTessMemberResultCallback_2_5(obj, member, p1, p2); +} +#endif + +template +class _TessMemberResultCallback_2_5 : public TessResultCallback5 { + public: + typedef TessResultCallback5 base; + typedef R (T::*MemberSignature)(P1,P2,A1,A2,A3,A4,A5) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessMemberResultCallback_2_5( T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + R result = (object_->*member_)(p1_,p2_,a1,a2,a3,a4,a5); + return result; + } else { + R result = (object_->*member_)(p1_,p2_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + return result; + } + } +}; + +template +class _TessMemberResultCallback_2_5 : public TessCallback5 { + public: + typedef TessCallback5 base; + typedef void (T::*MemberSignature)(P1,P2,A1,A2,A3,A4,A5) ; + + private: + T* object_; + MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessMemberResultCallback_2_5( T* object, MemberSignature member, P1 p1, P2 p2) + : object_(object), + member_(member), p1_(p1), p2_(p2) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + (object_->*member_)(p1_,p2_,a1,a2,a3,a4,a5); + } else { + (object_->*member_)(p1_,p2_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + member_ = NULL; + delete this; + } + } +}; + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_2_5::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,A1,A2,A3,A4,A5) , typename Identity::type p1, typename Identity::type p2) { + return new _TessMemberResultCallback_2_5(obj, member, p1, p2); +} +#endif + +#ifndef SWIG +template +inline typename _TessMemberResultCallback_2_5::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,A1,A2,A3,A4,A5) , typename Identity::type p1, typename Identity::type p2) { + return new _TessMemberResultCallback_2_5(obj, member, p1, p2); +} +#endif + +template +class _TessFunctionResultCallback_2_5 : public TessResultCallback5 { + public: + typedef TessResultCallback5 base; + typedef R (*FunctionSignature)(P1,P2,A1,A2,A3,A4,A5); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessFunctionResultCallback_2_5(FunctionSignature function, P1 p1, P2 p2) + : function_(function), p1_(p1), p2_(p2) { } + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + R result = (*function_)(p1_,p2_,a1,a2,a3,a4,a5); + return result; + } else { + R result = (*function_)(p1_,p2_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + return result; + } + } }; -template -class TessResultCallback1 { +template +class _TessFunctionResultCallback_2_5 : public TessCallback5 { public: - virtual ~TessResultCallback1() { } - virtual R Run(A1) = 0; + typedef TessCallback5 base; + typedef void (*FunctionSignature)(P1,P2,A1,A2,A3,A4,A5); + + private: + FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + + public: + inline _TessFunctionResultCallback_2_5(FunctionSignature function, P1 p1, P2 p2) + : function_(function), p1_(p1), p2_(p2) { } + + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { + if (!del) { + (*function_)(p1_,p2_,a1,a2,a3,a4,a5); + } else { + (*function_)(p1_,p2_,a1,a2,a3,a4,a5); + // zero out the pointer to ensure segfault if used again + function_ = NULL; + delete this; + } + } }; -template -class _ConstTessMemberResultCallback_0_1 : public TessResultCallback1 { +template +inline typename _TessFunctionResultCallback_2_5::base* +NewTessCallback(R (*function)(P1,P2,A1,A2,A3,A4,A5), typename Identity::type p1, typename Identity::type p2) { + return new _TessFunctionResultCallback_2_5(function, p1, p2); +} + +template +inline typename _TessFunctionResultCallback_2_5::base* +NewPermanentTessCallback(R (*function)(P1,P2,A1,A2,A3,A4,A5), typename Identity::type p1, typename Identity::type p2) { + return new _TessFunctionResultCallback_2_5(function, p1, p2); +} + +template +class _ConstTessMemberResultCallback_3_5 : public TessResultCallback5 { public: - typedef TessResultCallback1 base; - typedef R (T::*MemberSignature)(A1) const; + typedef TessResultCallback5 base; + typedef R (T::*MemberSignature)(P1,P2,P3,A1,A2,A3,A4,A5) const; private: const T* object_; MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; public: - inline _ConstTessMemberResultCallback_0_1( - const T* object, MemberSignature member) + inline _ConstTessMemberResultCallback_3_5(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) : object_(object), - member_(member) { - } + member_(member), p1_(p1), p2_(p2), p3_(p3) { } - virtual R Run(A1 a1) { + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { if (!del) { - R result = (object_->*member_)(a1); + R result = (object_->*member_)(p1_,p2_,p3_,a1,a2,a3,a4,a5); return result; } else { - R result = (object_->*member_)(a1); + R result = (object_->*member_)(p1_,p2_,p3_,a1,a2,a3,a4,a5); // zero out the pointer to ensure segfault if used again member_ = NULL; delete this; @@ -315,29 +8809,29 @@ class _ConstTessMemberResultCallback_0_1 : public TessResultCallback1 { } }; -template -class _ConstTessMemberResultCallback_0_1 - : public TessCallback1 { +template +class _ConstTessMemberResultCallback_3_5 : public TessCallback5 { public: - typedef TessCallback1 base; - typedef void (T::*MemberSignature)(A1) const; + typedef TessCallback5 base; + typedef void (T::*MemberSignature)(P1,P2,P3,A1,A2,A3,A4,A5) const; private: const T* object_; MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; public: - inline _ConstTessMemberResultCallback_0_1( - const T* object, MemberSignature member) + inline _ConstTessMemberResultCallback_3_5(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) : object_(object), - member_(member) { - } + member_(member), p1_(p1), p2_(p2), p3_(p3) { } - virtual void Run(A1 a1) { + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { if (!del) { - (object_->*member_)(a1); + (object_->*member_)(p1_,p2_,p3_,a1,a2,a3,a4,a5); } else { - (object_->*member_)(a1); + (object_->*member_)(p1_,p2_,p3_,a1,a2,a3,a4,a5); // zero out the pointer to ensure segfault if used again member_ = NULL; delete this; @@ -346,48 +8840,45 @@ class _ConstTessMemberResultCallback_0_1 }; #ifndef SWIG -template -inline typename _ConstTessMemberResultCallback_0_1::base* -NewTessCallback( - const T1* obj, R (T2::*member)(A1) const) { - return new _ConstTessMemberResultCallback_0_1( - obj, member); +template +inline typename _ConstTessMemberResultCallback_3_5::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,A1,A2,A3,A4,A5) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _ConstTessMemberResultCallback_3_5(obj, member, p1, p2, p3); } #endif #ifndef SWIG -template -inline typename _ConstTessMemberResultCallback_0_1::base* -NewPermanentTessCallback( - const T1* obj, R (T2::*member)(A1) const) { - return new _ConstTessMemberResultCallback_0_1( - obj, member); +template +inline typename _ConstTessMemberResultCallback_3_5::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,A1,A2,A3,A4,A5) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _ConstTessMemberResultCallback_3_5(obj, member, p1, p2, p3); } #endif -template -class _TessMemberResultCallback_0_1 : public TessResultCallback1 { +template +class _TessMemberResultCallback_3_5 : public TessResultCallback5 { public: - typedef TessResultCallback1 base; - typedef R (T::*MemberSignature)(A1) ; + typedef TessResultCallback5 base; + typedef R (T::*MemberSignature)(P1,P2,P3,A1,A2,A3,A4,A5) ; private: T* object_; MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; public: - inline _TessMemberResultCallback_0_1( - T* object, MemberSignature member) + inline _TessMemberResultCallback_3_5( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) : object_(object), - member_(member) { - } + member_(member), p1_(p1), p2_(p2), p3_(p3) { } - virtual R Run(A1 a1) { + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { if (!del) { - R result = (object_->*member_)(a1); + R result = (object_->*member_)(p1_,p2_,p3_,a1,a2,a3,a4,a5); return result; } else { - R result = (object_->*member_)(a1); + R result = (object_->*member_)(p1_,p2_,p3_,a1,a2,a3,a4,a5); // zero out the pointer to ensure segfault if used again member_ = NULL; delete this; @@ -396,29 +8887,29 @@ class _TessMemberResultCallback_0_1 : public TessResultCallback1 { } }; -template -class _TessMemberResultCallback_0_1 - : public TessCallback1 { +template +class _TessMemberResultCallback_3_5 : public TessCallback5 { public: - typedef TessCallback1 base; - typedef void (T::*MemberSignature)(A1) ; + typedef TessCallback5 base; + typedef void (T::*MemberSignature)(P1,P2,P3,A1,A2,A3,A4,A5) ; private: T* object_; MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; public: - inline _TessMemberResultCallback_0_1( - T* object, MemberSignature member) + inline _TessMemberResultCallback_3_5( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3) : object_(object), - member_(member) { - } + member_(member), p1_(p1), p2_(p2), p3_(p3) { } - virtual void Run(A1 a1) { + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { if (!del) { - (object_->*member_)(a1); + (object_->*member_)(p1_,p2_,p3_,a1,a2,a3,a4,a5); } else { - (object_->*member_)(a1); + (object_->*member_)(p1_,p2_,p3_,a1,a2,a3,a4,a5); // zero out the pointer to ensure segfault if used again member_ = NULL; delete this; @@ -427,46 +8918,43 @@ class _TessMemberResultCallback_0_1 }; #ifndef SWIG -template -inline typename _TessMemberResultCallback_0_1::base* -NewTessCallback( - T1* obj, R (T2::*member)(A1) ) { - return new _TessMemberResultCallback_0_1( - obj, member); +template +inline typename _TessMemberResultCallback_3_5::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,A1,A2,A3,A4,A5) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessMemberResultCallback_3_5(obj, member, p1, p2, p3); } #endif #ifndef SWIG -template -inline typename _TessMemberResultCallback_0_1::base* -NewPermanentTessCallback( - T1* obj, R (T2::*member)(A1) ) { - return new _TessMemberResultCallback_0_1( - obj, member); +template +inline typename _TessMemberResultCallback_3_5::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,A1,A2,A3,A4,A5) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessMemberResultCallback_3_5(obj, member, p1, p2, p3); } #endif -template -class _TessFunctionResultCallback_0_1 : public TessResultCallback1 { +template +class _TessFunctionResultCallback_3_5 : public TessResultCallback5 { public: - typedef TessResultCallback1 base; - typedef R (*FunctionSignature)(A1); + typedef TessResultCallback5 base; + typedef R (*FunctionSignature)(P1,P2,P3,A1,A2,A3,A4,A5); private: FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; public: - inline _TessFunctionResultCallback_0_1( - FunctionSignature function) - : function_(function) { - } + inline _TessFunctionResultCallback_3_5(FunctionSignature function, P1 p1, P2 p2, P3 p3) + : function_(function), p1_(p1), p2_(p2), p3_(p3) { } - virtual R Run(A1 a1) { + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { if (!del) { - R result = (*function_)(a1); + R result = (*function_)(p1_,p2_,p3_,a1,a2,a3,a4,a5); return result; } else { - R result = (*function_)(a1); + R result = (*function_)(p1_,p2_,p3_,a1,a2,a3,a4,a5); // zero out the pointer to ensure segfault if used again function_ = NULL; delete this; @@ -475,27 +8963,27 @@ class _TessFunctionResultCallback_0_1 : public TessResultCallback1 { } }; -template -class _TessFunctionResultCallback_0_1 - : public TessCallback1 { +template +class _TessFunctionResultCallback_3_5 : public TessCallback5 { public: - typedef TessCallback1 base; - typedef void (*FunctionSignature)(A1); + typedef TessCallback5 base; + typedef void (*FunctionSignature)(P1,P2,P3,A1,A2,A3,A4,A5); private: FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; public: - inline _TessFunctionResultCallback_0_1( - FunctionSignature function) - : function_(function) { - } + inline _TessFunctionResultCallback_3_5(FunctionSignature function, P1 p1, P2 p2, P3 p3) + : function_(function), p1_(p1), p2_(p2), p3_(p3) { } - virtual void Run(A1 a1) { + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { if (!del) { - (*function_)(a1); + (*function_)(p1_,p2_,p3_,a1,a2,a3,a4,a5); } else { - (*function_)(a1); + (*function_)(p1_,p2_,p3_,a1,a2,a3,a4,a5); // zero out the pointer to ensure segfault if used again function_ = NULL; delete this; @@ -503,55 +8991,43 @@ class _TessFunctionResultCallback_0_1 } }; -template -inline typename _TessFunctionResultCallback_0_1::base* -NewTessCallback(R (*function)(A1)) { - return new _TessFunctionResultCallback_0_1(function); +template +inline typename _TessFunctionResultCallback_3_5::base* +NewTessCallback(R (*function)(P1,P2,P3,A1,A2,A3,A4,A5), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessFunctionResultCallback_3_5(function, p1, p2, p3); } -template -inline typename _TessFunctionResultCallback_0_1::base* -NewPermanentTessCallback(R (*function)(A1)) { - return new _TessFunctionResultCallback_0_1(function); +template +inline typename _TessFunctionResultCallback_3_5::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,A1,A2,A3,A4,A5), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3) { + return new _TessFunctionResultCallback_3_5(function, p1, p2, p3); } -template -class TessCallback2 { - public: - virtual ~TessCallback2() { } - virtual void Run(A1,A2) = 0; -}; - -template -class TessResultCallback2 { - public: - virtual ~TessResultCallback2() { } - virtual R Run(A1,A2) = 0; -}; - -template -class _ConstTessMemberResultCallback_0_2 : public TessResultCallback2 { +template +class _ConstTessMemberResultCallback_4_5 : public TessResultCallback5 { public: - typedef TessResultCallback2 base; - typedef R (T::*MemberSignature)(A1,A2) const; + typedef TessResultCallback5 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,A1,A2,A3,A4,A5) const; private: const T* object_; MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; public: - inline _ConstTessMemberResultCallback_0_2( - const T* object, MemberSignature member) + inline _ConstTessMemberResultCallback_4_5(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) : object_(object), - member_(member) { - } + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } - virtual R Run(A1 a1,A2 a2) { + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { if (!del) { - R result = (object_->*member_)(a1,a2); + R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4,a5); return result; } else { - R result = (object_->*member_)(a1,a2); + R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4,a5); // zero out the pointer to ensure segfault if used again member_ = NULL; delete this; @@ -560,29 +9036,30 @@ class _ConstTessMemberResultCallback_0_2 : public TessResultCallback2 { } }; -template -class _ConstTessMemberResultCallback_0_2 - : public TessCallback2 { +template +class _ConstTessMemberResultCallback_4_5 : public TessCallback5 { public: - typedef TessCallback2 base; - typedef void (T::*MemberSignature)(A1,A2) const; + typedef TessCallback5 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,A1,A2,A3,A4,A5) const; private: const T* object_; MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; public: - inline _ConstTessMemberResultCallback_0_2( - const T* object, MemberSignature member) + inline _ConstTessMemberResultCallback_4_5(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) : object_(object), - member_(member) { - } + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } - virtual void Run(A1 a1,A2 a2) { + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { if (!del) { - (object_->*member_)(a1,a2); + (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4,a5); } else { - (object_->*member_)(a1,a2); + (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4,a5); // zero out the pointer to ensure segfault if used again member_ = NULL; delete this; @@ -591,48 +9068,46 @@ class _ConstTessMemberResultCallback_0_2 }; #ifndef SWIG -template -inline typename _ConstTessMemberResultCallback_0_2::base* -NewTessCallback( - const T1* obj, R (T2::*member)(A1,A2) const) { - return new _ConstTessMemberResultCallback_0_2( - obj, member); +template +inline typename _ConstTessMemberResultCallback_4_5::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,A1,A2,A3,A4,A5) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _ConstTessMemberResultCallback_4_5(obj, member, p1, p2, p3, p4); } #endif #ifndef SWIG -template -inline typename _ConstTessMemberResultCallback_0_2::base* -NewPermanentTessCallback( - const T1* obj, R (T2::*member)(A1,A2) const) { - return new _ConstTessMemberResultCallback_0_2( - obj, member); +template +inline typename _ConstTessMemberResultCallback_4_5::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,A1,A2,A3,A4,A5) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _ConstTessMemberResultCallback_4_5(obj, member, p1, p2, p3, p4); } #endif -template -class _TessMemberResultCallback_0_2 : public TessResultCallback2 { +template +class _TessMemberResultCallback_4_5 : public TessResultCallback5 { public: - typedef TessResultCallback2 base; - typedef R (T::*MemberSignature)(A1,A2) ; + typedef TessResultCallback5 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,A1,A2,A3,A4,A5) ; private: T* object_; MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; public: - inline _TessMemberResultCallback_0_2( - T* object, MemberSignature member) + inline _TessMemberResultCallback_4_5( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) : object_(object), - member_(member) { - } + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } - virtual R Run(A1 a1,A2 a2) { + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { if (!del) { - R result = (object_->*member_)(a1,a2); + R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4,a5); return result; } else { - R result = (object_->*member_)(a1,a2); + R result = (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4,a5); // zero out the pointer to ensure segfault if used again member_ = NULL; delete this; @@ -641,29 +9116,30 @@ class _TessMemberResultCallback_0_2 : public TessResultCallback2 { } }; -template -class _TessMemberResultCallback_0_2 - : public TessCallback2 { +template +class _TessMemberResultCallback_4_5 : public TessCallback5 { public: - typedef TessCallback2 base; - typedef void (T::*MemberSignature)(A1,A2) ; + typedef TessCallback5 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,A1,A2,A3,A4,A5) ; private: T* object_; MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; public: - inline _TessMemberResultCallback_0_2( - T* object, MemberSignature member) + inline _TessMemberResultCallback_4_5( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4) : object_(object), - member_(member) { - } + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } - virtual void Run(A1 a1,A2 a2) { + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { if (!del) { - (object_->*member_)(a1,a2); + (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4,a5); } else { - (object_->*member_)(a1,a2); + (object_->*member_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4,a5); // zero out the pointer to ensure segfault if used again member_ = NULL; delete this; @@ -672,46 +9148,44 @@ class _TessMemberResultCallback_0_2 }; #ifndef SWIG -template -inline typename _TessMemberResultCallback_0_2::base* -NewTessCallback( - T1* obj, R (T2::*member)(A1,A2) ) { - return new _TessMemberResultCallback_0_2( - obj, member); +template +inline typename _TessMemberResultCallback_4_5::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,A1,A2,A3,A4,A5) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessMemberResultCallback_4_5(obj, member, p1, p2, p3, p4); } #endif #ifndef SWIG -template -inline typename _TessMemberResultCallback_0_2::base* -NewPermanentTessCallback( - T1* obj, R (T2::*member)(A1,A2) ) { - return new _TessMemberResultCallback_0_2( - obj, member); +template +inline typename _TessMemberResultCallback_4_5::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,A1,A2,A3,A4,A5) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessMemberResultCallback_4_5(obj, member, p1, p2, p3, p4); } #endif -template -class _TessFunctionResultCallback_0_2 : public TessResultCallback2 { +template +class _TessFunctionResultCallback_4_5 : public TessResultCallback5 { public: - typedef TessResultCallback2 base; - typedef R (*FunctionSignature)(A1,A2); + typedef TessResultCallback5 base; + typedef R (*FunctionSignature)(P1,P2,P3,P4,A1,A2,A3,A4,A5); private: FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; public: - inline _TessFunctionResultCallback_0_2( - FunctionSignature function) - : function_(function) { - } + inline _TessFunctionResultCallback_4_5(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } - virtual R Run(A1 a1,A2 a2) { + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { if (!del) { - R result = (*function_)(a1,a2); + R result = (*function_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4,a5); return result; } else { - R result = (*function_)(a1,a2); + R result = (*function_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4,a5); // zero out the pointer to ensure segfault if used again function_ = NULL; delete this; @@ -720,27 +9194,28 @@ class _TessFunctionResultCallback_0_2 : public TessResultCallback2 { } }; -template -class _TessFunctionResultCallback_0_2 - : public TessCallback2 { +template +class _TessFunctionResultCallback_4_5 : public TessCallback5 { public: - typedef TessCallback2 base; - typedef void (*FunctionSignature)(A1,A2); + typedef TessCallback5 base; + typedef void (*FunctionSignature)(P1,P2,P3,P4,A1,A2,A3,A4,A5); private: FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; public: - inline _TessFunctionResultCallback_0_2( - FunctionSignature function) - : function_(function) { - } + inline _TessFunctionResultCallback_4_5(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4) { } - virtual void Run(A1 a1,A2 a2) { + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { if (!del) { - (*function_)(a1,a2); + (*function_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4,a5); } else { - (*function_)(a1,a2); + (*function_)(p1_,p2_,p3_,p4_,a1,a2,a3,a4,a5); // zero out the pointer to ensure segfault if used again function_ = NULL; delete this; @@ -748,55 +9223,45 @@ class _TessFunctionResultCallback_0_2 } }; -template -inline typename _TessFunctionResultCallback_0_2::base* -NewTessCallback(R (*function)(A1,A2)) { - return new _TessFunctionResultCallback_0_2(function); +template +inline typename _TessFunctionResultCallback_4_5::base* +NewTessCallback(R (*function)(P1,P2,P3,P4,A1,A2,A3,A4,A5), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessFunctionResultCallback_4_5(function, p1, p2, p3, p4); } -template -inline typename _TessFunctionResultCallback_0_2::base* -NewPermanentTessCallback(R (*function)(A1,A2)) { - return new _TessFunctionResultCallback_0_2(function); +template +inline typename _TessFunctionResultCallback_4_5::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,P4,A1,A2,A3,A4,A5), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4) { + return new _TessFunctionResultCallback_4_5(function, p1, p2, p3, p4); } -template -class TessCallback3 { - public: - virtual ~TessCallback3() { } - virtual void Run(A1,A2,A3) = 0; -}; - -template -class TessResultCallback3 { - public: - virtual ~TessResultCallback3() { } - virtual R Run(A1,A2,A3) = 0; -}; - -template -class _ConstTessMemberResultCallback_0_3 : public TessResultCallback3 { +template +class _ConstTessMemberResultCallback_5_5 : public TessResultCallback5 { public: - typedef TessResultCallback3 base; - typedef R (T::*MemberSignature)(A1,A2,A3) const; + typedef TessResultCallback5 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,A1,A2,A3,A4,A5) const; private: const T* object_; MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; public: - inline _ConstTessMemberResultCallback_0_3( - const T* object, MemberSignature member) + inline _ConstTessMemberResultCallback_5_5(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) : object_(object), - member_(member) { - } + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } - virtual R Run(A1 a1,A2 a2,A3 a3) { + + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { if (!del) { - R result = (object_->*member_)(a1,a2,a3); + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4,a5); return result; } else { - R result = (object_->*member_)(a1,a2,a3); + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4,a5); // zero out the pointer to ensure segfault if used again member_ = NULL; delete this; @@ -805,29 +9270,31 @@ class _ConstTessMemberResultCallback_0_3 : public TessResultCallback3 -class _ConstTessMemberResultCallback_0_3 - : public TessCallback3 { +template +class _ConstTessMemberResultCallback_5_5 : public TessCallback5 { public: - typedef TessCallback3 base; - typedef void (T::*MemberSignature)(A1,A2,A3) const; + typedef TessCallback5 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,A1,A2,A3,A4,A5) const; private: const T* object_; MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; public: - inline _ConstTessMemberResultCallback_0_3( - const T* object, MemberSignature member) + inline _ConstTessMemberResultCallback_5_5(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) : object_(object), - member_(member) { - } + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } - virtual void Run(A1 a1,A2 a2,A3 a3) { + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { if (!del) { - (object_->*member_)(a1,a2,a3); + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4,a5); } else { - (object_->*member_)(a1,a2,a3); + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4,a5); // zero out the pointer to ensure segfault if used again member_ = NULL; delete this; @@ -836,48 +9303,47 @@ class _ConstTessMemberResultCallback_0_3 }; #ifndef SWIG -template -inline typename _ConstTessMemberResultCallback_0_3::base* -NewTessCallback( - const T1* obj, R (T2::*member)(A1,A2,A3) const) { - return new _ConstTessMemberResultCallback_0_3( - obj, member); +template +inline typename _ConstTessMemberResultCallback_5_5::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,A1,A2,A3,A4,A5) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _ConstTessMemberResultCallback_5_5(obj, member, p1, p2, p3, p4, p5); } #endif #ifndef SWIG -template -inline typename _ConstTessMemberResultCallback_0_3::base* -NewPermanentTessCallback( - const T1* obj, R (T2::*member)(A1,A2,A3) const) { - return new _ConstTessMemberResultCallback_0_3( - obj, member); +template +inline typename _ConstTessMemberResultCallback_5_5::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,A1,A2,A3,A4,A5) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _ConstTessMemberResultCallback_5_5(obj, member, p1, p2, p3, p4, p5); } #endif -template -class _TessMemberResultCallback_0_3 : public TessResultCallback3 { +template +class _TessMemberResultCallback_5_5 : public TessResultCallback5 { public: - typedef TessResultCallback3 base; - typedef R (T::*MemberSignature)(A1,A2,A3) ; + typedef TessResultCallback5 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,A1,A2,A3,A4,A5) ; private: T* object_; MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; public: - inline _TessMemberResultCallback_0_3( - T* object, MemberSignature member) + inline _TessMemberResultCallback_5_5( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) : object_(object), - member_(member) { - } + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } - virtual R Run(A1 a1,A2 a2,A3 a3) { + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { if (!del) { - R result = (object_->*member_)(a1,a2,a3); + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4,a5); return result; } else { - R result = (object_->*member_)(a1,a2,a3); + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4,a5); // zero out the pointer to ensure segfault if used again member_ = NULL; delete this; @@ -886,29 +9352,31 @@ class _TessMemberResultCallback_0_3 : public TessResultCallback3 { } }; -template -class _TessMemberResultCallback_0_3 - : public TessCallback3 { +template +class _TessMemberResultCallback_5_5 : public TessCallback5 { public: - typedef TessCallback3 base; - typedef void (T::*MemberSignature)(A1,A2,A3) ; + typedef TessCallback5 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,A1,A2,A3,A4,A5) ; private: T* object_; MemberSignature member_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; public: - inline _TessMemberResultCallback_0_3( - T* object, MemberSignature member) + inline _TessMemberResultCallback_5_5( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) : object_(object), - member_(member) { - } + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } - virtual void Run(A1 a1,A2 a2,A3 a3) { + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { if (!del) { - (object_->*member_)(a1,a2,a3); + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4,a5); } else { - (object_->*member_)(a1,a2,a3); + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4,a5); // zero out the pointer to ensure segfault if used again member_ = NULL; delete this; @@ -917,46 +9385,45 @@ class _TessMemberResultCallback_0_3 }; #ifndef SWIG -template -inline typename _TessMemberResultCallback_0_3::base* -NewTessCallback( - T1* obj, R (T2::*member)(A1,A2,A3) ) { - return new _TessMemberResultCallback_0_3( - obj, member); +template +inline typename _TessMemberResultCallback_5_5::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,A1,A2,A3,A4,A5) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessMemberResultCallback_5_5(obj, member, p1, p2, p3, p4, p5); } #endif #ifndef SWIG -template -inline typename _TessMemberResultCallback_0_3::base* -NewPermanentTessCallback( - T1* obj, R (T2::*member)(A1,A2,A3) ) { - return new _TessMemberResultCallback_0_3( - obj, member); +template +inline typename _TessMemberResultCallback_5_5::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,A1,A2,A3,A4,A5) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessMemberResultCallback_5_5(obj, member, p1, p2, p3, p4, p5); } #endif -template -class _TessFunctionResultCallback_0_3 : public TessResultCallback3 { +template +class _TessFunctionResultCallback_5_5 : public TessResultCallback5 { public: - typedef TessResultCallback3 base; - typedef R (*FunctionSignature)(A1,A2,A3); + typedef TessResultCallback5 base; + typedef R (*FunctionSignature)(P1,P2,P3,P4,P5,A1,A2,A3,A4,A5); private: FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; public: - inline _TessFunctionResultCallback_0_3( - FunctionSignature function) - : function_(function) { - } + inline _TessFunctionResultCallback_5_5(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } - virtual R Run(A1 a1,A2 a2,A3 a3) { + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { if (!del) { - R result = (*function_)(a1,a2,a3); + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4,a5); return result; } else { - R result = (*function_)(a1,a2,a3); + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4,a5); // zero out the pointer to ensure segfault if used again function_ = NULL; delete this; @@ -965,27 +9432,29 @@ class _TessFunctionResultCallback_0_3 : public TessResultCallback3 { } }; -template -class _TessFunctionResultCallback_0_3 - : public TessCallback3 { +template +class _TessFunctionResultCallback_5_5 : public TessCallback5 { public: - typedef TessCallback3 base; - typedef void (*FunctionSignature)(A1,A2,A3); + typedef TessCallback5 base; + typedef void (*FunctionSignature)(P1,P2,P3,P4,P5,A1,A2,A3,A4,A5); private: FunctionSignature function_; + typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; public: - inline _TessFunctionResultCallback_0_3( - FunctionSignature function) - : function_(function) { - } + inline _TessFunctionResultCallback_5_5(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5) { } - virtual void Run(A1 a1,A2 a2,A3 a3) { + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { if (!del) { - (*function_)(a1,a2,a3); + (*function_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4,a5); } else { - (*function_)(a1,a2,a3); + (*function_)(p1_,p2_,p3_,p4_,p5_,a1,a2,a3,a4,a5); // zero out the pointer to ensure segfault if used again function_ = NULL; delete this; @@ -993,53 +9462,45 @@ class _TessFunctionResultCallback_0_3 } }; -template -inline typename _TessFunctionResultCallback_0_3::base* -NewTessCallback(R (*function)(A1,A2,A3)) { - return new _TessFunctionResultCallback_0_3(function); +template +inline typename _TessFunctionResultCallback_5_5::base* +NewTessCallback(R (*function)(P1,P2,P3,P4,P5,A1,A2,A3,A4,A5), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessFunctionResultCallback_5_5(function, p1, p2, p3, p4, p5); } -template -inline typename _TessFunctionResultCallback_0_3::base* -NewPermanentTessCallback(R (*function)(A1,A2,A3)) { - return new _TessFunctionResultCallback_0_3(function); +template +inline typename _TessFunctionResultCallback_5_5::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,P4,P5,A1,A2,A3,A4,A5), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5) { + return new _TessFunctionResultCallback_5_5(function, p1, p2, p3, p4, p5); } -// Specified by TR1 [4.7.2] Reference modifications. -template struct remove_reference; -template struct remove_reference { typedef T type; }; -template struct remove_reference { typedef T type; }; - -// Identity::type is a typedef of T. Useful for preventing the -// compiler from inferring the type of an argument in templates. -template -struct Identity { - typedef T type; -}; - -template -class _ConstTessMemberResultCallback_1_3 - : public TessResultCallback3 { +template +class _ConstTessMemberResultCallback_6_5 : public TessResultCallback5 { public: - typedef TessResultCallback3 base; - typedef R (T::*MemberSignature)(P1,A1,A2,A3) const; + typedef TessResultCallback5 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4,A5) const; private: - T* object_; + const T* object_; MemberSignature member_; typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; public: - inline _ConstTessMemberResultCallback_1_3(T* object, - MemberSignature member, P1 p1) - : object_(object), member_(member), p1_(p1) { } + inline _ConstTessMemberResultCallback_6_5(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } - virtual R Run(A1 a1, A2 a2, A3 a3) { + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { if (!del) { - R result = (object_->*member_)(p1_,a1,a2,a3); + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4,a5); return result; } else { - R result = (object_->*member_)(p1_,a1,a2,a3); + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4,a5); // zero out the pointer to ensure segfault if used again member_ = NULL; delete this; @@ -1048,28 +9509,32 @@ class _ConstTessMemberResultCallback_1_3 } }; -template -class _ConstTessMemberResultCallback_1_3 - : public TessCallback3 { +template +class _ConstTessMemberResultCallback_6_5 : public TessCallback5 { public: - typedef TessCallback3 base; - typedef void (T::*MemberSignature)(P1,A1,A2,A3) const; + typedef TessCallback5 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4,A5) const; private: - T* object_; + const T* object_; MemberSignature member_; typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; public: - inline _ConstTessMemberResultCallback_1_3(T* object, - MemberSignature member, P1 p1) - : object_(object), member_(member), p1_(p1) { } + inline _ConstTessMemberResultCallback_6_5(const T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } - virtual void Run(A1 a1, A2 a2, A3 a3) { + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { if (!del) { - (object_->*member_)(p1_,a1,a2,a3); + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4,a5); } else { - (object_->*member_)(p1_,a1,a2,a3); + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4,a5); // zero out the pointer to ensure segfault if used again member_ = NULL; delete this; @@ -1078,43 +9543,48 @@ class _ConstTessMemberResultCallback_1_3 }; #ifndef SWIG -template -inline typename _ConstTessMemberResultCallback_1_3::base* -NewTessCallback( T1* obj, R (T2::*member)(P1,A1,A2,A3) , typename Identity::type p1) { - return new _ConstTessMemberResultCallback_1_3(obj, member, p1); +template +inline typename _ConstTessMemberResultCallback_6_5::base* +NewTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4,A5) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _ConstTessMemberResultCallback_6_5(obj, member, p1, p2, p3, p4, p5, p6); } #endif #ifndef SWIG -template -inline typename _ConstTessMemberResultCallback_1_3::base* -NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,A1,A2,A3) , typename Identity::type p1) { - return new _ConstTessMemberResultCallback_1_3(obj, member, p1); +template +inline typename _ConstTessMemberResultCallback_6_5::base* +NewPermanentTessCallback(const T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4,A5) const, typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _ConstTessMemberResultCallback_6_5(obj, member, p1, p2, p3, p4, p5, p6); } #endif -template -class _TessMemberResultCallback_1_3 : public TessResultCallback3 { +template +class _TessMemberResultCallback_6_5 : public TessResultCallback5 { public: - typedef TessResultCallback3 base; - typedef R (T::*MemberSignature)(P1,A1,A2,A3) ; + typedef TessResultCallback5 base; + typedef R (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4,A5) ; private: T* object_; MemberSignature member_; typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; public: - inline _TessMemberResultCallback_1_3(T* object, - MemberSignature member, P1 p1) - : object_(object), member_(member), p1_(p1) { } + inline _TessMemberResultCallback_6_5( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } - virtual R Run(A1 a1, A2 a2, A3 a3) { + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { if (!del) { - R result = (object_->*member_)(p1_,a1,a2,a3); + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4,a5); return result; } else { - R result = (object_->*member_)(p1_,a1,a2,a3); + R result = (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4,a5); // zero out the pointer to ensure segfault if used again member_ = NULL; delete this; @@ -1123,28 +9593,32 @@ class _TessMemberResultCallback_1_3 : public TessResultCallback3 { } }; -template -class _TessMemberResultCallback_1_3 - : public TessCallback3 { +template +class _TessMemberResultCallback_6_5 : public TessCallback5 { public: - typedef TessCallback3 base; - typedef void (T::*MemberSignature)(P1,A1,A2,A3) ; + typedef TessCallback5 base; + typedef void (T::*MemberSignature)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4,A5) ; private: T* object_; MemberSignature member_; typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; public: - inline _TessMemberResultCallback_1_3(T* object, - MemberSignature member, P1 p1) - : object_(object), member_(member), p1_(p1) { } + inline _TessMemberResultCallback_6_5( T* object, MemberSignature member, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : object_(object), + member_(member), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } - virtual void Run(A1 a1, A2 a2, A3 a3) { + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { if (!del) { - (object_->*member_)(p1_,a1,a2,a3); + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4,a5); } else { - (object_->*member_)(p1_,a1,a2,a3); + (object_->*member_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4,a5); // zero out the pointer to ensure segfault if used again member_ = NULL; delete this; @@ -1153,41 +9627,46 @@ class _TessMemberResultCallback_1_3 }; #ifndef SWIG -template -inline typename _TessMemberResultCallback_1_3::base* -NewTessCallback( T1* obj, R (T2::*member)(P1,A1,A2,A3) , typename Identity::type p1) { - return new _TessMemberResultCallback_1_3(obj, member, p1); +template +inline typename _TessMemberResultCallback_6_5::base* +NewTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4,A5) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessMemberResultCallback_6_5(obj, member, p1, p2, p3, p4, p5, p6); } #endif #ifndef SWIG -template -inline typename _TessMemberResultCallback_1_3::base* -NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,A1,A2,A3) , typename Identity::type p1) { - return new _TessMemberResultCallback_1_3(obj, member, p1); +template +inline typename _TessMemberResultCallback_6_5::base* +NewPermanentTessCallback( T1* obj, R (T2::*member)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4,A5) , typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessMemberResultCallback_6_5(obj, member, p1, p2, p3, p4, p5, p6); } #endif -template -class _TessFunctionResultCallback_1_3 : public TessCallback3 { +template +class _TessFunctionResultCallback_6_5 : public TessResultCallback5 { public: - typedef TessCallback3 base; - typedef R (*FunctionSignature)(P1,A1,A2,A3); + typedef TessResultCallback5 base; + typedef R (*FunctionSignature)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4,A5); private: FunctionSignature function_; typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; public: - inline _TessFunctionResultCallback_1_3(FunctionSignature function, P1 p1) - : function_(function), p1_(p1) { } + inline _TessFunctionResultCallback_6_5(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } - virtual R Run(A1 a1, A2 a2, A3 a3) { + virtual R Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { if (!del) { - R result = (*function_)(p1_,a1,a2,a3); + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4,a5); return result; } else { - R result = (*function_)(p1_,a1,a2,a3); + R result = (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4,a5); // zero out the pointer to ensure segfault if used again function_ = NULL; delete this; @@ -1196,26 +9675,30 @@ class _TessFunctionResultCallback_1_3 : public TessCallback3 { } }; -template -class _TessFunctionResultCallback_1_3 - : public TessCallback3 { +template +class _TessFunctionResultCallback_6_5 : public TessCallback5 { public: - typedef TessCallback3 base; - typedef void (*FunctionSignature)(P1,A1,A2,A3); + typedef TessCallback5 base; + typedef void (*FunctionSignature)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4,A5); private: FunctionSignature function_; typename remove_reference::type p1_; + typename remove_reference::type p2_; + typename remove_reference::type p3_; + typename remove_reference::type p4_; + typename remove_reference::type p5_; + typename remove_reference::type p6_; public: - inline _TessFunctionResultCallback_1_3(FunctionSignature function, P1 p1) - : function_(function), p1_(p1) { } + inline _TessFunctionResultCallback_6_5(FunctionSignature function, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + : function_(function), p1_(p1), p2_(p2), p3_(p3), p4_(p4), p5_(p5), p6_(p6) { } - virtual void Run(A1 a1, A2 a2, A3 a3) { + virtual void Run(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5) { if (!del) { - (*function_)(p1_,a1,a2,a3); + (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4,a5); } else { - (*function_)(p1_,a1,a2,a3); + (*function_)(p1_,p2_,p3_,p4_,p5_,p6_,a1,a2,a3,a4,a5); // zero out the pointer to ensure segfault if used again function_ = NULL; delete this; @@ -1223,16 +9706,16 @@ class _TessFunctionResultCallback_1_3 } }; -template -inline typename _TessFunctionResultCallback_1_3::base* -NewTessCallback(R (*function)(P1,A1,A2,A3), typename Identity::type p1) { - return new _TessFunctionResultCallback_1_3(function, p1); +template +inline typename _TessFunctionResultCallback_6_5::base* +NewTessCallback(R (*function)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4,A5), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessFunctionResultCallback_6_5(function, p1, p2, p3, p4, p5, p6); } -template -inline typename _TessFunctionResultCallback_1_3::base* -NewPermanentTessCallback(R (*function)(P1,A1,A2,A3), typename Identity::type p1) { - return new _TessFunctionResultCallback_1_3(function, p1); +template +inline typename _TessFunctionResultCallback_6_5::base* +NewPermanentTessCallback(R (*function)(P1,P2,P3,P4,P5,P6,A1,A2,A3,A4,A5), typename Identity::type p1, typename Identity::type p2, typename Identity::type p3, typename Identity::type p4, typename Identity::type p5, typename Identity::type p6) { + return new _TessFunctionResultCallback_6_5(function, p1, p2, p3, p4, p5, p6); } #endif /* _TESS_CALLBACK_SPECIALIZATIONS_H */ diff --git a/ccutil/tessdatamanager.cpp b/ccutil/tessdatamanager.cpp index 924b8eb600..ac9c6c86f1 100644 --- a/ccutil/tessdatamanager.cpp +++ b/ccutil/tessdatamanager.cpp @@ -35,6 +35,7 @@ namespace tesseract { bool TessdataManager::Init(const char *data_file_name, int debug_level) { int i; debug_level_ = debug_level; + data_file_name_ = data_file_name; data_file_ = fopen(data_file_name, "rb"); if (data_file_ == NULL) { tprintf("Error opening data file %s\n", data_file_name); @@ -244,7 +245,7 @@ bool TessdataManager::ExtractToFile(const char *filename) { FILE *output_file = fopen(filename, "wb"); if (output_file == NULL) { - tprintf("Error openning %s\n", filename); + tprintf("Error opening %s\n", filename); exit(1); } inT64 begin_offset = ftell(GetDataFilePtr()); diff --git a/ccutil/tessdatamanager.h b/ccutil/tessdatamanager.h index b5ed6628ea..7d7bed0119 100644 --- a/ccutil/tessdatamanager.h +++ b/ccutil/tessdatamanager.h @@ -21,7 +21,9 @@ #define TESSERACT_CCUTIL_TESSDATAMANAGER_H_ #include + #include "host.h" +#include "strngs.h" #include "tprintf.h" static const char kTrainedDataSuffix[] = "traineddata"; @@ -44,7 +46,7 @@ static const char kCubeSystemDawgFileSuffix[] = "cube-word-dawg"; static const char kShapeTableFileSuffix[] = "shapetable"; static const char kBigramDawgFileSuffix[] = "bigram-dawg"; static const char kUnambigDawgFileSuffix[] = "unambig-dawg"; -static const char kParamsTrainingModelFileSuffix[] = "params-training-model"; +static const char kParamsModelFileSuffix[] = "params-model"; namespace tesseract { @@ -59,13 +61,13 @@ enum TessdataType { TESSDATA_SYSTEM_DAWG, // 7 TESSDATA_NUMBER_DAWG, // 8 TESSDATA_FREQ_DAWG, // 9 - TESSDATA_FIXED_LENGTH_DAWGS, // 10 + TESSDATA_FIXED_LENGTH_DAWGS, // 10 // deprecated TESSDATA_CUBE_UNICHARSET, // 11 TESSDATA_CUBE_SYSTEM_DAWG, // 12 TESSDATA_SHAPE_TABLE, // 13 TESSDATA_BIGRAM_DAWG, // 14 TESSDATA_UNAMBIG_DAWG, // 15 - TESSDATA_PARAMS_TRAINING_MODEL, // 16 + TESSDATA_PARAMS_MODEL, // 16 TESSDATA_NUM_ENTRIES }; @@ -85,13 +87,13 @@ static const char * const kTessdataFileSuffixes[] = { kSystemDawgFileSuffix, // 7 kNumberDawgFileSuffix, // 8 kFreqDawgFileSuffix, // 9 - kFixedLengthDawgsFileSuffix, // 10 + kFixedLengthDawgsFileSuffix, // 10 // deprecated kCubeUnicharsetFileSuffix, // 11 kCubeSystemDawgFileSuffix, // 12 kShapeTableFileSuffix, // 13 kBigramDawgFileSuffix, // 14 kUnambigDawgFileSuffix, // 15 - kParamsTrainingModelFileSuffix, // 16 + kParamsModelFileSuffix, // 16 }; /** @@ -109,13 +111,13 @@ static const bool kTessdataFileIsText[] = { false, // 7 false, // 8 false, // 9 - false, // 10 + false, // 10 // deprecated true, // 11 false, // 12 false, // 13 false, // 14 false, // 15 - false, // 16 + true, // 16 }; /** @@ -146,6 +148,9 @@ class TessdataManager { */ bool Init(const char *data_file_name, int debug_level); + // Return the name of the underlying data file. + const STRING &GetDataFileName() const { return data_file_name_; } + /** Returns data file pointer. */ inline FILE *GetDataFilePtr() const { return data_file_; } @@ -279,6 +284,7 @@ class TessdataManager { * when new tessdata types are introduced. */ inT32 actual_tessdata_num_entries_; + STRING data_file_name_; // name of the data file. FILE *data_file_; ///< pointer to the data file. int debug_level_; // True if the bytes need swapping. diff --git a/ccutil/tprintf.cpp b/ccutil/tprintf.cpp index 1778137906..456b6faebb 100644 --- a/ccutil/tprintf.cpp +++ b/ccutil/tprintf.cpp @@ -24,43 +24,46 @@ #include #include -#include "strngs.h" +#include "ccutil.h" #include "params.h" +#include "strngs.h" #include "tprintf.h" -#include "ccutil.h" #define MAX_MSG_LEN 65536 #define EXTERN -// Since tprintf is protected by a mutex, these parameters can rmain global. +// Since tprintf is protected by a mutex, these parameters can remain global. DLLSYM STRING_VAR(debug_file, "", "File to send tprintf output to"); +DLLSYM INT_VAR(FLAGS_v, 0, "Minimum logging level for tlog() output"); + DLLSYM void -tprintf( // Trace printf -const char *format, ... // special message +tprintf_internal( // Trace printf + const int level, // Logging level + const char *format, ... // Message ) { + if (FLAGS_v < level) return; tesseract::tprintfMutex.Lock(); - va_list args; //variable args - static FILE *debugfp = NULL; //debug file - //debug window - inT32 offset = 0; //into message + va_list args; // variable args + static FILE *debugfp = NULL; // debug file + // debug window + inT32 offset = 0; // into message static char msg[MAX_MSG_LEN + 1]; - va_start(args, format); //variable list + va_start(args, format); // variable list + // Format into msg #ifdef _WIN32 - //Format into msg - offset += _vsnprintf (msg + offset, MAX_MSG_LEN - offset, format, args); + offset += _vsnprintf(msg + offset, MAX_MSG_LEN - offset, format, args); if (strcmp(debug_file.string(), "/dev/null") == 0) debug_file.set_value("nul"); #else - //Format into msg - offset += vsprintf (msg + offset, format, args); + offset += vsnprintf(msg + offset, MAX_MSG_LEN - offset, format, args); #endif va_end(args); - if (debugfp == NULL && strlen (debug_file.string ()) > 0) { - debugfp = fopen (debug_file.string (), "wb"); - } else if (debugfp != NULL && strlen (debug_file.string ()) == 0) { + if (debugfp == NULL && strlen(debug_file.string()) > 0) { + debugfp = fopen(debug_file.string(), "wb"); + } else if (debugfp != NULL && strlen(debug_file.string()) == 0) { fclose(debugfp); debugfp = NULL; } @@ -70,46 +73,3 @@ const char *format, ... // special message fprintf(stderr, "%s", msg); tesseract::tprintfMutex.Unlock(); } - - -/************************************************************************* - * pause_continue() - * UI for a debugging pause - to see an intermediate state - * Returns TRUE to continue as normal to the next pause in the current mode; - * FALSE to quit the current pausing mode. - *************************************************************************/ - -DLLSYM BOOL8 - //special message -pause_continue (const char *format, ... -) { - va_list args; //variable args - char msg[1000]; - STRING str = STRING ("DEBUG PAUSE:\n"); - - va_start(args, format); //variable list - vsprintf(msg, format, args); //Format into msg - va_end(args); - - #ifdef GRAPHICS_DISABLED - // No interaction allowed -> simply go on - return true; - #else - - #ifdef __UNIX__ - printf ("%s\n", msg); - printf ("Type \"c\" to cancel, anything else to continue: "); - char c = getchar (); - return (c != 'c'); - #endif - - #ifdef _WIN32 - str += - STRING (msg) + STRING ("\nUse OK to continue, CANCEL to stop pausing"); - // return AfxMessageBox( str.string(), MB_OKCANCEL ) == IDOK; - return::MessageBox (NULL, msg, "IMGAPP", - MB_APPLMODAL | MB_OKCANCEL) == IDOK; - #endif - - #endif -} diff --git a/ccutil/tprintf.h b/ccutil/tprintf.h index ed742816ff..fba96e7098 100644 --- a/ccutil/tprintf.h +++ b/ccutil/tprintf.h @@ -17,19 +17,29 @@ * **********************************************************************/ -#ifndef TPRINTF_H -#define TPRINTF_H - -#include "params.h" - -extern DLLSYM STRING_VAR_H (debug_file, "", "File to send tprintf output to"); -extern DLLSYM BOOL_VAR_H (debug_window_on, TRUE, -"Send tprintf to window unless file set"); - -extern TESS_API void tprintf( // Trace printf -const char *format, ... // special message -); - // special message -DLLSYM BOOL8 pause_continue (const char *format, ... -); -#endif +#ifndef TESSERACT_CCUTIL_TPRINTF_H +#define TESSERACT_CCUTIL_TPRINTF_H + +#include "params.h" + +extern DLLSYM STRING_VAR_H(debug_file, "", + "File to send tprintf output to"); +extern DLLSYM BOOL_VAR_H(debug_window_on, TRUE, + "Send tprintf to window unless file set"); + +// Main logging function. +#define tprintf(args...) tprintf_internal(0, args) + +// Variant guarded by the numeric logging level parameter FLAGS_v (default 0). +// Code using ParseCommandLineFlags() can control its value using the --v +// commandline argument. Otherwise it must be specified in a config file like +// other params. +#define tlog(level, args...) tprintf_internal(level, args) + +#define TLOG_IS_ON(level) (FLAGS_v >= level) + +extern TESS_API void tprintf_internal( // Trace printf + const int level, // Logging level + const char *format, ...); // Message + +#endif // define TESSERACT_CCUTIL_TPRINTF_H diff --git a/ccutil/universalambigs.cpp b/ccutil/universalambigs.cpp new file mode 100644 index 0000000000..f89d9e8a7d --- /dev/null +++ b/ccutil/universalambigs.cpp @@ -0,0 +1,21370 @@ +/////////////////////////////////////////////////////////////////////// +// File: universalambigs.cpp +// Description: Data for a universal ambigs file that is useful for +// any language. +// Author: Ray Smith +// Created: Mon Mar 18 11:26:00 PDT 2013 +// +// (C) Copyright 2013, Google Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +/////////////////////////////////////////////////////////////////////// + +namespace tesseract { + +extern const char kUniversalAmbigsFile[] = { + '\166', '\062', '\012', '\047', '\047', '\040', '\042', '\040', + '\061', '\012', '\140', '\047', '\040', '\042', '\040', '\061', + '\012', '\047', '\140', '\040', '\042', '\040', '\061', '\012', + '\342', '\200', '\230', '\047', '\040', '\042', '\040', '\061', + '\012', '\047', '\342', '\200', '\230', '\040', '\042', '\040', + '\061', '\012', '\342', '\200', '\231', '\047', '\040', '\042', + '\040', '\061', '\012', '\047', '\342', '\200', '\231', '\040', + '\042', '\040', '\061', '\012', '\140', '\140', '\040', '\042', + '\040', '\061', '\012', '\140', '\342', '\200', '\230', '\040', + '\042', '\040', '\061', '\012', '\342', '\200', '\230', '\140', + '\040', '\042', '\040', '\061', '\012', '\140', '\342', '\200', + '\231', '\040', '\042', '\040', '\061', '\012', '\342', '\200', + '\231', '\140', '\040', '\042', '\040', '\061', '\012', '\342', + '\200', '\230', '\342', '\200', '\230', '\040', '\342', '\200', + '\234', '\040', '\061', '\012', '\342', '\200', '\230', '\342', + '\200', '\231', '\040', '\042', '\040', '\061', '\012', '\342', + '\200', '\231', '\342', '\200', '\230', '\040', '\042', '\040', + '\061', '\012', '\342', '\200', '\231', '\342', '\200', '\231', + '\040', '\342', '\200', '\235', '\040', '\061', '\012', '\054', + '\054', '\040', '\342', '\200', '\236', '\040', '\061', '\012', + '\155', '\040', '\162', '\156', '\040', '\060', '\012', '\162', + '\156', '\040', '\155', '\040', '\060', '\012', '\155', '\040', + '\151', '\156', '\040', '\060', '\012', '\151', '\156', '\040', + '\155', '\040', '\060', '\012', '\144', '\040', '\143', '\154', + '\040', '\060', '\012', '\143', '\154', '\040', '\144', '\040', + '\060', '\012', '\156', '\156', '\040', '\162', '\155', '\040', + '\060', '\012', '\162', '\155', '\040', '\156', '\156', '\040', + '\060', '\012', '\156', '\040', '\162', '\151', '\040', '\060', + '\012', '\162', '\151', '\040', '\156', '\040', '\060', '\012', + '\154', '\151', '\040', '\150', '\040', '\060', '\012', '\154', + '\162', '\040', '\150', '\040', '\060', '\012', '\151', '\151', + '\040', '\165', '\040', '\060', '\012', '\151', '\151', '\040', + '\156', '\040', '\060', '\012', '\156', '\151', '\040', '\155', + '\040', '\060', '\012', '\151', '\151', '\151', '\040', '\155', + '\040', '\060', '\012', '\154', '\154', '\040', '\110', '\040', + '\060', '\012', '\111', '\055', '\111', '\040', '\110', '\040', + '\060', '\012', '\166', '\166', '\040', '\167', '\040', '\060', + '\012', '\126', '\126', '\040', '\127', '\040', '\060', '\012', + '\164', '\040', '\146', '\040', '\060', '\012', '\146', '\040', + '\164', '\040', '\060', '\012', '\141', '\040', '\157', '\040', + '\060', '\012', '\157', '\040', '\141', '\040', '\060', '\012', + '\145', '\040', '\143', '\040', '\060', '\012', '\143', '\040', + '\145', '\040', '\060', '\012', '\162', '\162', '\040', '\156', + '\040', '\060', '\012', '\105', '\040', '\146', '\151', '\040', + '\060', '\012', '\154', '\074', '\040', '\153', '\040', '\060', + '\012', '\154', '\144', '\040', '\153', '\151', '\040', '\060', + '\012', '\154', '\170', '\040', '\150', '\040', '\060', '\012', + '\170', '\156', '\040', '\155', '\040', '\060', '\012', '\165', + '\170', '\040', '\151', '\156', '\040', '\060', '\012', '\162', + '\040', '\164', '\040', '\060', '\012', '\144', '\040', '\164', + '\154', '\040', '\060', '\012', '\144', '\151', '\040', '\164', + '\150', '\040', '\060', '\012', '\165', '\162', '\040', '\151', + '\156', '\040', '\060', '\012', '\165', '\156', '\040', '\151', + '\155', '\040', '\060', '\012', '\165', '\040', '\141', '\040', + '\060', '\012', '\157', '\040', '\303', '\263', '\040', '\060', + '\012', '\303', '\263', '\040', '\157', '\040', '\060', '\012', + '\151', '\040', '\303', '\255', '\040', '\060', '\012', '\303', + '\255', '\040', '\151', '\040', '\060', '\012', '\141', '\040', + '\303', '\241', '\040', '\060', '\012', '\303', '\241', '\040', + '\141', '\040', '\060', '\012', '\145', '\040', '\303', '\251', + '\040', '\060', '\012', '\303', '\251', '\040', '\145', '\040', + '\060', '\012', '\165', '\040', '\303', '\272', '\040', '\060', + '\012', '\303', '\272', '\040', '\165', '\040', '\060', '\012', + '\156', '\040', '\303', '\261', '\040', '\060', '\012', '\303', + '\261', '\040', '\156', '\040', '\060', '\012', '\060', '\040', + '\157', '\040', '\060', '\012', '\144', '\040', '\164', '\162', + '\040', '\060', '\012', '\156', '\040', '\164', '\162', '\040', + '\060', '\012', '\303', '\261', '\040', '\146', '\151', '\040', + '\060', '\012', '\165', '\040', '\164', '\151', '\040', '\060', + '\012', '\303', '\261', '\040', '\164', '\151', '\040', '\060', + '\012', '\144', '\040', '\164', '\151', '\040', '\060', '\012', + '\144', '\040', '\164', '\303', '\255', '\040', '\060', '\012', + '\144', '\040', '\162', '\303', '\255', '\040', '\060', '\012', + '\141', '\040', '\303', '\240', '\040', '\060', '\012', '\145', + '\040', '\303', '\250', '\040', '\060', '\012', '\156', '\040', + '\151', '\152', '\040', '\060', '\012', '\147', '\040', '\151', + '\152', '\040', '\060', '\012', '\157', '\040', '\303', '\262', + '\040', '\060', '\012', '\105', '\040', '\303', '\211', '\040', + '\060', '\012', '\105', '\040', '\303', '\210', '\040', '\060', + '\012', '\165', '\040', '\303', '\274', '\040', '\060', '\012', + '\170', '\156', '\105', '\040', '\141', '\156', '\040', '\061', + '\012', '\155', '\131', '\170', '\040', '\155', '\145', '\040', + '\061', '\012', '\161', '\164', '\105', '\040', '\156', '\164', + '\040', '\061', '\012', '\124', '\154', '\142', '\040', '\154', + '\145', '\040', '\061', '\012', '\166', '\170', '\116', '\040', + '\166', '\141', '\040', '\061', '\012', '\147', '\152', '\121', + '\040', '\156', '\147', '\040', '\061', '\012', '\152', '\160', + '\106', '\040', '\151', '\152', '\040', '\061', '\012', '\131', + '\162', '\154', '\040', '\154', '\145', '\040', '\061', '\012', + '\141', '\161', '\131', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\166', '\112', '\040', '\166', '\141', '\040', + '\061', '\012', '\146', '\142', '\114', '\040', '\142', '\145', + '\040', '\061', '\012', '\116', '\166', '\153', '\040', '\166', + '\141', '\040', '\061', '\012', '\146', '\112', '\160', '\040', + '\160', '\162', '\040', '\061', '\012', '\167', '\170', '\103', + '\040', '\167', '\141', '\040', '\061', '\012', '\143', '\165', + '\112', '\040', '\161', '\165', '\040', '\061', '\012', '\121', + '\172', '\164', '\040', '\164', '\141', '\040', '\061', '\012', + '\161', '\113', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\163', '\143', '\112', '\040', '\163', '\164', '\040', + '\061', '\012', '\160', '\130', '\160', '\040', '\160', '\157', + '\040', '\061', '\012', '\126', '\161', '\151', '\040', '\164', + '\151', '\040', '\061', '\012', '\125', '\170', '\153', '\040', + '\153', '\141', '\040', '\061', '\012', '\153', '\112', '\166', + '\040', '\153', '\141', '\040', '\061', '\012', '\131', '\153', + '\144', '\040', '\153', '\141', '\040', '\061', '\012', '\166', + '\160', '\130', '\040', '\166', '\141', '\040', '\061', '\012', + '\151', '\102', '\166', '\040', '\164', '\151', '\040', '\061', + '\012', '\172', '\122', '\142', '\040', '\163', '\172', '\040', + '\061', '\012', '\171', '\124', '\155', '\040', '\155', '\151', + '\040', '\061', '\012', '\155', '\113', '\160', '\040', '\160', + '\162', '\040', '\061', '\012', '\126', '\172', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\130', '\164', '\160', + '\040', '\164', '\151', '\040', '\061', '\012', '\155', '\166', + '\104', '\040', '\166', '\141', '\040', '\061', '\012', '\155', + '\104', '\161', '\040', '\155', '\145', '\040', '\061', '\012', + '\152', '\170', '\120', '\040', '\151', '\152', '\040', '\061', + '\012', '\102', '\170', '\166', '\040', '\166', '\141', '\040', + '\061', '\012', '\157', '\111', '\165', '\040', '\161', '\165', + '\040', '\061', '\012', '\122', '\166', '\143', '\040', '\166', + '\141', '\040', '\061', '\012', '\165', '\103', '\152', '\040', + '\161', '\165', '\040', '\061', '\012', '\157', '\101', '\157', + '\040', '\166', '\157', '\040', '\061', '\012', '\161', '\165', + '\102', '\040', '\164', '\165', '\040', '\061', '\012', '\142', + '\164', '\126', '\040', '\164', '\151', '\040', '\061', '\012', + '\114', '\155', '\143', '\040', '\155', '\145', '\040', '\061', + '\012', '\164', '\126', '\167', '\040', '\164', '\151', '\040', + '\061', '\012', '\131', '\170', '\166', '\040', '\166', '\141', + '\040', '\061', '\012', '\110', '\170', '\155', '\040', '\155', + '\145', '\040', '\061', '\012', '\144', '\126', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\170', '\131', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\165', '\120', + '\152', '\040', '\164', '\165', '\040', '\061', '\012', '\146', + '\124', '\146', '\040', '\146', '\157', '\040', '\061', '\012', + '\122', '\152', '\167', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\144', '\101', '\040', '\144', '\151', '\040', + '\061', '\012', '\152', '\172', '\116', '\040', '\151', '\152', + '\040', '\061', '\012', '\155', '\170', '\114', '\040', '\155', + '\145', '\040', '\061', '\012', '\171', '\147', '\112', '\040', + '\156', '\147', '\040', '\061', '\012', '\126', '\166', '\147', + '\040', '\166', '\141', '\040', '\061', '\012', '\162', '\152', + '\113', '\040', '\151', '\152', '\040', '\061', '\012', '\171', + '\165', '\126', '\040', '\164', '\165', '\040', '\061', '\012', + '\163', '\127', '\153', '\040', '\153', '\165', '\040', '\061', + '\012', '\120', '\147', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\152', '\110', '\155', '\040', '\155', '\145', + '\040', '\061', '\012', '\172', '\153', '\125', '\040', '\153', + '\165', '\040', '\061', '\012', '\147', '\166', '\107', '\040', + '\166', '\141', '\040', '\061', '\012', '\150', '\144', '\120', + '\040', '\164', '\150', '\040', '\061', '\012', '\155', '\126', + '\142', '\040', '\155', '\145', '\040', '\061', '\012', '\121', + '\147', '\144', '\040', '\144', '\151', '\040', '\061', '\012', + '\172', '\143', '\132', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\161', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\172', '\163', '\112', '\040', '\163', '\172', + '\040', '\061', '\012', '\144', '\146', '\116', '\040', '\144', + '\151', '\040', '\061', '\012', '\144', '\147', '\127', '\040', + '\144', '\151', '\040', '\061', '\012', '\167', '\116', '\162', + '\040', '\162', '\151', '\040', '\061', '\012', '\172', '\166', + '\103', '\040', '\166', '\141', '\040', '\061', '\012', '\161', + '\131', '\167', '\040', '\161', '\165', '\040', '\061', '\012', + '\165', '\110', '\171', '\040', '\164', '\165', '\040', '\061', + '\012', '\164', '\116', '\161', '\040', '\164', '\150', '\040', + '\061', '\012', '\154', '\170', '\112', '\040', '\154', '\151', + '\040', '\061', '\012', '\110', '\142', '\153', '\040', '\153', + '\165', '\040', '\061', '\012', '\170', '\163', '\107', '\040', + '\163', '\164', '\040', '\061', '\012', '\166', '\123', '\142', + '\040', '\166', '\141', '\040', '\061', '\012', '\170', '\106', + '\142', '\040', '\142', '\165', '\040', '\061', '\012', '\116', + '\164', '\147', '\040', '\164', '\150', '\040', '\061', '\012', + '\157', '\102', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\153', '\166', '\040', '\161', '\165', '\040', + '\061', '\012', '\142', '\126', '\152', '\040', '\151', '\152', + '\040', '\061', '\012', '\172', '\152', '\124', '\040', '\151', + '\152', '\040', '\061', '\012', '\142', '\166', '\130', '\040', + '\166', '\141', '\040', '\061', '\012', '\157', '\132', '\146', + '\040', '\164', '\157', '\040', '\061', '\012', '\153', '\143', + '\125', '\040', '\153', '\157', '\040', '\061', '\012', '\146', + '\106', '\155', '\040', '\155', '\145', '\040', '\061', '\012', + '\130', '\142', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\113', '\161', '\166', '\040', '\166', '\141', '\040', + '\061', '\012', '\122', '\167', '\152', '\040', '\151', '\152', + '\040', '\061', '\012', '\144', '\166', '\112', '\040', '\166', + '\141', '\040', '\061', '\012', '\172', '\156', '\112', '\040', + '\163', '\172', '\040', '\061', '\012', '\161', '\161', '\126', + '\040', '\161', '\165', '\040', '\061', '\012', '\160', '\170', + '\115', '\040', '\160', '\157', '\040', '\061', '\012', '\145', + '\102', '\152', '\040', '\151', '\152', '\040', '\061', '\012', + '\155', '\112', '\170', '\040', '\155', '\145', '\040', '\061', + '\012', '\170', '\156', '\115', '\040', '\156', '\147', '\040', + '\061', '\012', '\141', '\103', '\161', '\040', '\166', '\141', + '\040', '\061', '\012', '\160', '\110', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\164', '\146', '\121', '\040', + '\164', '\150', '\040', '\061', '\012', '\167', '\161', '\156', + '\040', '\161', '\165', '\040', '\061', '\012', '\155', '\123', + '\163', '\040', '\151', '\163', '\040', '\061', '\012', '\163', + '\102', '\167', '\040', '\163', '\164', '\040', '\061', '\012', + '\106', '\150', '\156', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\116', '\142', '\040', '\163', '\172', '\040', + '\061', '\012', '\115', '\166', '\142', '\040', '\166', '\141', + '\040', '\061', '\012', '\142', '\126', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\161', '\110', '\164', '\040', + '\164', '\150', '\040', '\061', '\012', '\161', '\114', '\166', + '\040', '\161', '\165', '\040', '\061', '\012', '\153', '\147', + '\106', '\040', '\156', '\147', '\040', '\061', '\012', '\166', + '\170', '\127', '\040', '\166', '\141', '\040', '\061', '\012', + '\143', '\144', '\131', '\040', '\143', '\150', '\040', '\061', + '\012', '\130', '\162', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\105', '\146', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\154', '\161', '\111', '\040', '\161', + '\165', '\040', '\061', '\012', '\114', '\172', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\172', '\150', '\130', + '\040', '\164', '\150', '\040', '\061', '\012', '\147', '\150', + '\132', '\040', '\164', '\150', '\040', '\061', '\012', '\154', + '\106', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\166', '\126', '\143', '\040', '\166', '\141', '\040', '\061', + '\012', '\154', '\115', '\162', '\040', '\145', '\162', '\040', + '\061', '\012', '\124', '\161', '\152', '\040', '\161', '\165', + '\040', '\061', '\012', '\152', '\101', '\170', '\040', '\151', + '\152', '\040', '\061', '\012', '\151', '\115', '\164', '\040', + '\164', '\150', '\040', '\061', '\012', '\116', '\154', '\166', + '\040', '\166', '\141', '\040', '\061', '\012', '\172', '\142', + '\120', '\040', '\163', '\172', '\040', '\061', '\012', '\153', + '\126', '\170', '\040', '\153', '\141', '\040', '\061', '\012', + '\145', '\121', '\154', '\040', '\164', '\145', '\040', '\061', + '\012', '\163', '\127', '\142', '\040', '\163', '\164', '\040', + '\061', '\012', '\102', '\161', '\171', '\040', '\161', '\165', + '\040', '\061', '\012', '\144', '\130', '\153', '\040', '\153', + '\141', '\040', '\061', '\012', '\166', '\125', '\143', '\040', + '\166', '\141', '\040', '\061', '\012', '\166', '\117', '\142', + '\040', '\166', '\141', '\040', '\061', '\012', '\165', '\110', + '\146', '\040', '\161', '\165', '\040', '\061', '\012', '\161', + '\116', '\162', '\040', '\161', '\165', '\040', '\061', '\012', + '\165', '\106', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\115', '\154', '\162', '\040', '\145', '\162', '\040', + '\061', '\012', '\153', '\155', '\132', '\040', '\153', '\141', + '\040', '\061', '\012', '\163', '\122', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\127', '\161', '\166', '\040', + '\161', '\165', '\040', '\061', '\012', '\150', '\146', '\113', + '\040', '\164', '\150', '\040', '\061', '\012', '\166', '\170', + '\121', '\040', '\166', '\141', '\040', '\061', '\012', '\154', + '\103', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\146', '\131', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\164', '\146', '\123', '\040', '\164', '\150', '\040', + '\061', '\012', '\161', '\144', '\117', '\040', '\161', '\165', + '\040', '\061', '\012', '\144', '\121', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\170', '\144', '\130', '\040', + '\144', '\145', '\040', '\061', '\012', '\155', '\116', '\170', + '\040', '\155', '\145', '\040', '\061', '\012', '\153', '\106', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\167', + '\152', '\123', '\040', '\151', '\152', '\040', '\061', '\012', + '\171', '\120', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\167', '\143', '\127', '\040', '\143', '\150', '\040', + '\061', '\012', '\116', '\152', '\172', '\040', '\163', '\172', + '\040', '\061', '\012', '\144', '\126', '\160', '\040', '\144', + '\145', '\040', '\061', '\012', '\144', '\161', '\104', '\040', + '\161', '\165', '\040', '\061', '\012', '\162', '\112', '\163', + '\040', '\163', '\172', '\040', '\061', '\012', '\170', '\160', + '\110', '\040', '\160', '\157', '\040', '\061', '\012', '\170', + '\161', '\122', '\040', '\161', '\165', '\040', '\061', '\012', + '\147', '\126', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\102', '\164', '\161', '\040', '\164', '\150', '\040', + '\061', '\012', '\156', '\155', '\102', '\040', '\156', '\164', + '\040', '\061', '\012', '\172', '\143', '\115', '\040', '\163', + '\172', '\040', '\061', '\012', '\143', '\146', '\107', '\040', + '\143', '\150', '\040', '\061', '\012', '\155', '\146', '\117', + '\040', '\155', '\145', '\040', '\061', '\012', '\131', '\150', + '\143', '\040', '\164', '\150', '\040', '\061', '\012', '\142', + '\132', '\155', '\040', '\155', '\145', '\040', '\061', '\012', + '\155', '\172', '\102', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\122', '\167', '\040', '\166', '\141', '\040', + '\061', '\012', '\171', '\104', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\132', '\147', '\146', '\040', '\156', + '\147', '\040', '\061', '\012', '\153', '\161', '\124', '\040', + '\161', '\165', '\040', '\061', '\012', '\111', '\165', '\172', + '\040', '\161', '\165', '\040', '\061', '\012', '\162', '\142', + '\127', '\040', '\145', '\162', '\040', '\061', '\012', '\112', + '\155', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\113', '\166', '\152', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\143', '\104', '\040', '\143', '\150', '\040', + '\061', '\012', '\170', '\147', '\103', '\040', '\156', '\147', + '\040', '\061', '\012', '\152', '\103', '\170', '\040', '\151', + '\152', '\040', '\061', '\012', '\142', '\127', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\171', '\167', '\127', + '\040', '\167', '\141', '\040', '\061', '\012', '\112', '\153', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\170', + '\107', '\163', '\040', '\163', '\172', '\040', '\061', '\012', + '\166', '\142', '\110', '\040', '\166', '\141', '\040', '\061', + '\012', '\154', '\124', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\145', '\103', '\142', '\040', '\145', '\162', + '\040', '\061', '\012', '\152', '\126', '\166', '\040', '\166', + '\141', '\040', '\061', '\012', '\152', '\104', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\152', '\157', '\121', + '\040', '\160', '\157', '\040', '\061', '\012', '\161', '\164', + '\115', '\040', '\164', '\150', '\040', '\061', '\012', '\122', + '\161', '\153', '\040', '\161', '\165', '\040', '\061', '\012', + '\110', '\166', '\147', '\040', '\166', '\141', '\040', '\061', + '\012', '\165', '\101', '\172', '\040', '\161', '\165', '\040', + '\061', '\012', '\155', '\146', '\127', '\040', '\155', '\145', + '\040', '\061', '\012', '\164', '\147', '\123', '\040', '\164', + '\150', '\040', '\061', '\012', '\143', '\161', '\104', '\040', + '\161', '\165', '\040', '\061', '\012', '\163', '\146', '\131', + '\040', '\163', '\172', '\040', '\061', '\012', '\131', '\150', + '\166', '\040', '\164', '\150', '\040', '\061', '\012', '\165', + '\161', '\115', '\040', '\161', '\165', '\040', '\061', '\012', + '\170', '\160', '\113', '\040', '\160', '\162', '\040', '\061', + '\012', '\112', '\172', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\143', '\121', '\153', '\040', '\143', '\150', + '\040', '\061', '\012', '\164', '\152', '\117', '\040', '\164', + '\150', '\040', '\061', '\012', '\161', '\170', '\132', '\040', + '\161', '\165', '\040', '\061', '\012', '\172', '\120', '\166', + '\040', '\163', '\172', '\040', '\061', '\012', '\161', '\116', + '\153', '\040', '\161', '\165', '\040', '\061', '\012', '\154', + '\166', '\121', '\040', '\166', '\141', '\040', '\061', '\012', + '\153', '\107', '\167', '\040', '\153', '\141', '\040', '\061', + '\012', '\170', '\165', '\104', '\040', '\161', '\165', '\040', + '\061', '\012', '\112', '\166', '\171', '\040', '\166', '\141', + '\040', '\061', '\012', '\152', '\131', '\145', '\040', '\164', + '\145', '\040', '\061', '\012', '\146', '\132', '\165', '\040', + '\161', '\165', '\040', '\061', '\012', '\161', '\131', '\157', + '\040', '\161', '\165', '\040', '\061', '\012', '\166', '\150', + '\111', '\040', '\164', '\150', '\040', '\061', '\012', '\146', + '\170', '\131', '\040', '\146', '\157', '\040', '\061', '\012', + '\171', '\120', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\146', '\107', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\144', '\155', '\124', '\040', '\155', '\145', + '\040', '\061', '\012', '\166', '\146', '\130', '\040', '\166', + '\141', '\040', '\061', '\012', '\170', '\121', '\164', '\040', + '\164', '\150', '\040', '\061', '\012', '\143', '\170', '\123', + '\040', '\143', '\150', '\040', '\061', '\012', '\166', '\172', + '\101', '\040', '\166', '\141', '\040', '\061', '\012', '\161', + '\141', '\101', '\040', '\161', '\165', '\040', '\061', '\012', + '\112', '\142', '\170', '\040', '\142', '\145', '\040', '\061', + '\012', '\153', '\126', '\144', '\040', '\153', '\141', '\040', + '\061', '\012', '\130', '\152', '\166', '\040', '\166', '\141', + '\040', '\061', '\012', '\150', '\153', '\111', '\040', '\164', + '\150', '\040', '\061', '\012', '\166', '\121', '\165', '\040', + '\161', '\165', '\040', '\061', '\012', '\166', '\150', '\113', + '\040', '\164', '\150', '\040', '\061', '\012', '\104', '\166', + '\152', '\040', '\166', '\141', '\040', '\061', '\012', '\126', + '\142', '\155', '\040', '\155', '\145', '\040', '\061', '\012', + '\146', '\160', '\116', '\040', '\160', '\162', '\040', '\061', + '\012', '\160', '\153', '\107', '\040', '\153', '\141', '\040', + '\061', '\012', '\142', '\114', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\164', '\112', '\143', '\040', '\164', + '\150', '\040', '\061', '\012', '\167', '\167', '\112', '\040', + '\167', '\141', '\040', '\061', '\012', '\132', '\162', '\167', + '\040', '\145', '\162', '\040', '\061', '\012', '\167', '\144', + '\127', '\040', '\144', '\145', '\040', '\061', '\012', '\127', + '\147', '\146', '\040', '\156', '\147', '\040', '\061', '\012', + '\120', '\161', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\147', '\116', '\040', '\156', '\147', '\040', + '\061', '\012', '\172', '\110', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\170', '\124', '\154', '\040', '\154', + '\145', '\040', '\061', '\012', '\104', '\166', '\164', '\040', + '\164', '\150', '\040', '\061', '\012', '\167', '\155', '\125', + '\040', '\155', '\145', '\040', '\061', '\012', '\170', '\150', + '\155', '\040', '\164', '\150', '\040', '\061', '\012', '\150', + '\103', '\170', '\040', '\164', '\150', '\040', '\061', '\012', + '\166', '\167', '\126', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\166', '\114', '\040', '\166', '\141', '\040', + '\061', '\012', '\156', '\107', '\146', '\040', '\156', '\164', + '\040', '\061', '\012', '\152', '\152', '\103', '\040', '\151', + '\152', '\040', '\061', '\012', '\125', '\143', '\147', '\040', + '\143', '\150', '\040', '\061', '\012', '\160', '\127', '\146', + '\040', '\160', '\162', '\040', '\061', '\012', '\152', '\170', + '\107', '\040', '\151', '\152', '\040', '\061', '\012', '\115', + '\161', '\156', '\040', '\161', '\165', '\040', '\061', '\012', + '\171', '\166', '\127', '\040', '\166', '\141', '\040', '\061', + '\012', '\154', '\127', '\153', '\040', '\153', '\141', '\040', + '\061', '\012', '\155', '\144', '\117', '\040', '\155', '\145', + '\040', '\061', '\012', '\161', '\116', '\155', '\040', '\161', + '\165', '\040', '\061', '\012', '\122', '\167', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\170', '\146', '\166', + '\040', '\166', '\141', '\040', '\061', '\012', '\165', '\117', + '\167', '\040', '\161', '\165', '\040', '\061', '\012', '\170', + '\150', '\132', '\040', '\164', '\150', '\040', '\061', '\012', + '\152', '\114', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\102', '\171', '\040', '\146', '\157', '\040', + '\061', '\012', '\156', '\125', '\152', '\040', '\156', '\164', + '\040', '\061', '\012', '\154', '\124', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\152', '\154', '\120', '\040', + '\151', '\152', '\040', '\061', '\012', '\167', '\162', '\122', + '\040', '\145', '\162', '\040', '\061', '\012', '\162', '\130', + '\167', '\040', '\145', '\162', '\040', '\061', '\012', '\145', + '\126', '\167', '\040', '\166', '\145', '\040', '\061', '\012', + '\172', '\127', '\156', '\040', '\156', '\147', '\040', '\061', + '\012', '\155', '\112', '\163', '\040', '\163', '\172', '\040', + '\061', '\012', '\115', '\147', '\171', '\040', '\156', '\147', + '\040', '\061', '\012', '\165', '\132', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\124', '\144', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\155', '\161', '\111', + '\040', '\161', '\165', '\040', '\061', '\012', '\104', '\150', + '\160', '\040', '\164', '\150', '\040', '\061', '\012', '\160', + '\155', '\113', '\040', '\155', '\145', '\040', '\061', '\012', + '\123', '\163', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\163', '\127', '\154', '\040', '\163', '\172', '\040', + '\061', '\012', '\151', '\161', '\113', '\040', '\161', '\165', + '\040', '\061', '\012', '\147', '\152', '\107', '\040', '\156', + '\147', '\040', '\061', '\012', '\144', '\152', '\102', '\040', + '\151', '\152', '\040', '\061', '\012', '\167', '\113', '\166', + '\040', '\166', '\141', '\040', '\061', '\012', '\167', '\166', + '\111', '\040', '\166', '\141', '\040', '\061', '\012', '\164', + '\143', '\125', '\040', '\164', '\150', '\040', '\061', '\012', + '\164', '\153', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\125', '\145', '\040', '\164', '\145', '\040', + '\061', '\012', '\154', '\125', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\156', '\102', '\147', '\040', '\156', + '\164', '\040', '\061', '\012', '\144', '\110', '\170', '\040', + '\144', '\145', '\040', '\061', '\012', '\127', '\142', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\166', '\165', + '\121', '\040', '\161', '\165', '\040', '\061', '\012', '\110', + '\160', '\154', '\040', '\154', '\145', '\040', '\061', '\012', + '\157', '\126', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\102', '\142', '\040', '\166', '\141', '\040', + '\061', '\012', '\124', '\144', '\172', '\040', '\163', '\172', + '\040', '\061', '\012', '\160', '\146', '\126', '\040', '\160', + '\162', '\040', '\061', '\012', '\161', '\147', '\116', '\040', + '\161', '\165', '\040', '\061', '\012', '\160', '\143', '\125', + '\040', '\143', '\150', '\040', '\061', '\012', '\147', '\143', + '\116', '\040', '\143', '\150', '\040', '\061', '\012', '\166', + '\153', '\101', '\040', '\166', '\141', '\040', '\061', '\012', + '\143', '\121', '\146', '\040', '\143', '\150', '\040', '\061', + '\012', '\131', '\172', '\170', '\040', '\163', '\172', '\040', + '\061', '\012', '\171', '\160', '\106', '\040', '\160', '\162', + '\040', '\061', '\012', '\166', '\102', '\167', '\040', '\166', + '\141', '\040', '\061', '\012', '\160', '\120', '\144', '\040', + '\144', '\145', '\040', '\061', '\012', '\161', '\155', '\125', + '\040', '\161', '\165', '\040', '\061', '\012', '\145', '\127', + '\146', '\040', '\166', '\145', '\040', '\061', '\012', '\152', + '\132', '\162', '\040', '\145', '\162', '\040', '\061', '\012', + '\110', '\167', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\171', '\171', '\111', '\040', '\156', '\171', '\040', + '\061', '\012', '\132', '\146', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\114', '\147', '\167', '\040', '\156', + '\147', '\040', '\061', '\012', '\165', '\161', '\160', '\040', + '\161', '\165', '\040', '\061', '\012', '\170', '\117', '\152', + '\040', '\151', '\152', '\040', '\061', '\012', '\144', '\153', + '\112', '\040', '\153', '\157', '\040', '\061', '\012', '\144', + '\161', '\115', '\040', '\161', '\165', '\040', '\061', '\012', + '\163', '\142', '\127', '\040', '\151', '\163', '\040', '\061', + '\012', '\172', '\115', '\160', '\040', '\163', '\172', '\040', + '\061', '\012', '\156', '\112', '\172', '\040', '\156', '\147', + '\040', '\061', '\012', '\153', '\115', '\143', '\040', '\153', + '\157', '\040', '\061', '\012', '\172', '\161', '\127', '\040', + '\161', '\165', '\040', '\061', '\012', '\166', '\121', '\153', + '\040', '\166', '\141', '\040', '\061', '\012', '\145', '\161', + '\104', '\040', '\161', '\165', '\040', '\061', '\012', '\150', + '\106', '\156', '\040', '\164', '\150', '\040', '\061', '\012', + '\166', '\143', '\132', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\107', '\153', '\040', '\153', '\141', '\040', + '\061', '\012', '\153', '\172', '\146', '\040', '\163', '\172', + '\040', '\061', '\012', '\170', '\132', '\170', '\040', '\170', + '\145', '\040', '\061', '\012', '\161', '\166', '\116', '\040', + '\161', '\165', '\040', '\061', '\012', '\171', '\153', '\131', + '\040', '\153', '\141', '\040', '\061', '\012', '\142', '\162', + '\110', '\040', '\145', '\162', '\040', '\061', '\012', '\127', + '\162', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\167', '\152', '\105', '\040', '\151', '\152', '\040', '\061', + '\012', '\153', '\152', '\121', '\040', '\153', '\141', '\040', + '\061', '\012', '\146', '\114', '\152', '\040', '\151', '\152', + '\040', '\061', '\012', '\155', '\147', '\105', '\040', '\156', + '\147', '\040', '\061', '\012', '\170', '\167', '\111', '\040', + '\167', '\141', '\040', '\061', '\012', '\151', '\104', '\167', + '\040', '\164', '\151', '\040', '\061', '\012', '\102', '\164', + '\170', '\040', '\164', '\150', '\040', '\061', '\012', '\166', + '\120', '\172', '\040', '\166', '\141', '\040', '\061', '\012', + '\171', '\161', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\106', '\145', '\040', '\145', '\162', '\040', + '\061', '\012', '\154', '\121', '\171', '\040', '\154', '\145', + '\040', '\061', '\012', '\147', '\102', '\160', '\040', '\156', + '\147', '\040', '\061', '\012', '\152', '\144', '\131', '\040', + '\144', '\145', '\040', '\061', '\012', '\164', '\166', '\121', + '\040', '\164', '\150', '\040', '\061', '\012', '\154', '\152', + '\117', '\040', '\154', '\145', '\040', '\061', '\012', '\116', + '\163', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\170', '\144', '\117', '\040', '\144', '\145', '\040', '\061', + '\012', '\147', '\172', '\127', '\040', '\156', '\147', '\040', + '\061', '\012', '\167', '\164', '\115', '\040', '\164', '\150', + '\040', '\061', '\012', '\161', '\146', '\122', '\040', '\161', + '\165', '\040', '\061', '\012', '\152', '\132', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\127', '\143', '\142', + '\040', '\143', '\150', '\040', '\061', '\012', '\144', '\166', + '\121', '\040', '\166', '\141', '\040', '\061', '\012', '\152', + '\110', '\142', '\040', '\151', '\152', '\040', '\061', '\012', + '\170', '\142', '\115', '\040', '\142', '\145', '\040', '\061', + '\012', '\156', '\127', '\147', '\040', '\156', '\164', '\040', + '\061', '\012', '\131', '\167', '\152', '\040', '\151', '\152', + '\040', '\061', '\012', '\130', '\167', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\160', '\170', '\113', '\040', + '\160', '\162', '\040', '\061', '\012', '\171', '\142', '\121', + '\040', '\142', '\145', '\040', '\061', '\012', '\127', '\166', + '\155', '\040', '\166', '\141', '\040', '\061', '\012', '\114', + '\147', '\172', '\040', '\156', '\147', '\040', '\061', '\012', + '\142', '\164', '\123', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\122', '\154', '\040', '\154', '\145', '\040', + '\061', '\012', '\161', '\161', '\112', '\040', '\161', '\165', + '\040', '\061', '\012', '\103', '\156', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\106', '\155', '\167', '\040', + '\155', '\145', '\040', '\061', '\012', '\144', '\166', '\120', + '\040', '\166', '\141', '\040', '\061', '\012', '\166', '\161', + '\102', '\040', '\161', '\165', '\040', '\061', '\012', '\144', + '\152', '\111', '\040', '\144', '\145', '\040', '\061', '\012', + '\152', '\126', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\166', '\132', '\040', '\166', '\141', '\040', + '\061', '\012', '\103', '\167', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\125', '\171', '\142', '\040', '\142', + '\145', '\040', '\061', '\012', '\106', '\146', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\163', '\157', '\130', + '\040', '\163', '\172', '\040', '\061', '\012', '\161', '\150', + '\122', '\040', '\164', '\150', '\040', '\061', '\012', '\146', + '\127', '\172', '\040', '\163', '\172', '\040', '\061', '\012', + '\166', '\162', '\130', '\040', '\166', '\141', '\040', '\061', + '\012', '\145', '\117', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\142', '\167', '\132', '\040', '\142', '\145', + '\040', '\061', '\012', '\144', '\156', '\126', '\040', '\156', + '\147', '\040', '\061', '\012', '\107', '\142', '\167', '\040', + '\142', '\145', '\040', '\061', '\012', '\170', '\107', '\144', + '\040', '\144', '\145', '\040', '\061', '\012', '\155', '\156', + '\132', '\040', '\156', '\147', '\040', '\061', '\012', '\142', + '\160', '\116', '\040', '\160', '\162', '\040', '\061', '\012', + '\144', '\172', '\130', '\040', '\144', '\145', '\040', '\061', + '\012', '\102', '\170', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\172', '\160', '\170', '\040', '\163', '\172', + '\040', '\061', '\012', '\144', '\161', '\132', '\040', '\161', + '\165', '\040', '\061', '\012', '\170', '\124', '\146', '\040', + '\146', '\157', '\040', '\061', '\012', '\167', '\120', '\166', + '\040', '\166', '\141', '\040', '\061', '\012', '\143', '\170', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\150', + '\144', '\124', '\040', '\164', '\150', '\040', '\061', '\012', + '\171', '\167', '\130', '\040', '\167', '\141', '\040', '\061', + '\012', '\125', '\166', '\166', '\040', '\166', '\141', '\040', + '\061', '\012', '\162', '\113', '\160', '\040', '\145', '\162', + '\040', '\061', '\012', '\163', '\144', '\106', '\040', '\144', + '\145', '\040', '\061', '\012', '\112', '\143', '\147', '\040', + '\143', '\150', '\040', '\061', '\012', '\170', '\172', '\117', + '\040', '\163', '\172', '\040', '\061', '\012', '\170', '\124', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\144', + '\152', '\120', '\040', '\144', '\145', '\040', '\061', '\012', + '\147', '\124', '\156', '\040', '\156', '\147', '\040', '\061', + '\012', '\107', '\164', '\160', '\040', '\164', '\150', '\040', + '\061', '\012', '\170', '\147', '\101', '\040', '\156', '\147', + '\040', '\061', '\012', '\142', '\144', '\114', '\040', '\144', + '\145', '\040', '\061', '\012', '\167', '\172', '\117', '\040', + '\163', '\172', '\040', '\061', '\012', '\146', '\150', '\111', + '\040', '\164', '\150', '\040', '\061', '\012', '\127', '\155', + '\160', '\040', '\155', '\145', '\040', '\061', '\012', '\121', + '\144', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\165', '\131', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\142', '\112', '\040', '\160', '\162', '\040', + '\061', '\012', '\152', '\122', '\144', '\040', '\144', '\145', + '\040', '\061', '\012', '\130', '\163', '\170', '\040', '\163', + '\172', '\040', '\061', '\012', '\172', '\147', '\111', '\040', + '\156', '\147', '\040', '\061', '\012', '\161', '\150', '\131', + '\040', '\164', '\150', '\040', '\061', '\012', '\107', '\147', + '\152', '\040', '\156', '\147', '\040', '\061', '\012', '\106', + '\152', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\121', '\167', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\172', '\170', '\127', '\040', '\163', '\172', '\040', + '\061', '\012', '\166', '\103', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\143', '\143', '\114', '\040', '\143', + '\150', '\040', '\061', '\012', '\113', '\170', '\163', '\040', + '\163', '\172', '\040', '\061', '\012', '\155', '\131', '\162', + '\040', '\145', '\162', '\040', '\061', '\012', '\162', '\121', + '\164', '\040', '\145', '\162', '\040', '\061', '\012', '\132', + '\170', '\163', '\040', '\163', '\172', '\040', '\061', '\012', + '\150', '\144', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\167', '\110', '\040', '\144', '\145', '\040', + '\061', '\012', '\131', '\155', '\154', '\040', '\154', '\145', + '\040', '\061', '\012', '\161', '\126', '\172', '\040', '\161', + '\165', '\040', '\061', '\012', '\122', '\166', '\154', '\040', + '\166', '\141', '\040', '\061', '\012', '\171', '\110', '\153', + '\040', '\153', '\141', '\040', '\061', '\012', '\127', '\152', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\150', + '\115', '\167', '\040', '\164', '\150', '\040', '\061', '\012', + '\160', '\172', '\125', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\143', '\114', '\040', '\143', '\150', '\040', + '\061', '\012', '\161', '\117', '\141', '\040', '\161', '\165', + '\040', '\061', '\012', '\145', '\161', '\111', '\040', '\161', + '\165', '\040', '\061', '\012', '\151', '\131', '\160', '\040', + '\164', '\151', '\040', '\061', '\012', '\166', '\103', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\165', '\157', + '\126', '\040', '\162', '\157', '\040', '\061', '\012', '\146', + '\132', '\170', '\040', '\146', '\157', '\040', '\061', '\012', + '\161', '\121', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\144', '\105', '\040', '\161', '\165', '\040', + '\061', '\012', '\161', '\127', '\170', '\040', '\161', '\165', + '\040', '\061', '\012', '\131', '\153', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\106', '\160', '\152', '\040', + '\151', '\152', '\040', '\061', '\012', '\172', '\107', '\166', + '\040', '\166', '\141', '\040', '\061', '\012', '\162', '\167', + '\117', '\040', '\145', '\162', '\040', '\061', '\012', '\121', + '\172', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\113', '\161', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\147', '\124', '\040', '\156', '\147', '\040', + '\061', '\012', '\152', '\163', '\132', '\040', '\163', '\172', + '\040', '\061', '\012', '\141', '\110', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\171', '\152', '\114', '\040', + '\151', '\152', '\040', '\061', '\012', '\131', '\143', '\167', + '\040', '\143', '\150', '\040', '\061', '\012', '\142', '\156', + '\120', '\040', '\141', '\156', '\040', '\061', '\012', '\166', + '\127', '\156', '\040', '\141', '\156', '\040', '\061', '\012', + '\172', '\171', '\131', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\122', '\163', '\040', '\163', '\164', '\040', + '\061', '\012', '\167', '\165', '\120', '\040', '\161', '\165', + '\040', '\061', '\012', '\166', '\152', '\102', '\040', '\166', + '\141', '\040', '\061', '\012', '\152', '\162', '\124', '\040', + '\145', '\162', '\040', '\061', '\012', '\166', '\167', '\112', + '\040', '\166', '\141', '\040', '\061', '\012', '\144', '\126', + '\152', '\040', '\144', '\145', '\040', '\061', '\012', '\172', + '\166', '\127', '\040', '\166', '\141', '\040', '\061', '\012', + '\144', '\132', '\153', '\040', '\144', '\145', '\040', '\061', + '\012', '\156', '\162', '\107', '\040', '\141', '\156', '\040', + '\061', '\012', '\161', '\163', '\125', '\040', '\161', '\165', + '\040', '\061', '\012', '\120', '\166', '\163', '\040', '\166', + '\141', '\040', '\061', '\012', '\154', '\114', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\161', '\103', '\172', + '\040', '\161', '\165', '\040', '\061', '\012', '\144', '\166', + '\126', '\040', '\144', '\145', '\040', '\061', '\012', '\120', + '\152', '\167', '\040', '\151', '\152', '\040', '\061', '\012', + '\113', '\155', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\112', '\146', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\156', '\167', '\131', '\040', '\141', '\156', + '\040', '\061', '\012', '\147', '\167', '\103', '\040', '\156', + '\147', '\040', '\061', '\012', '\166', '\107', '\142', '\040', + '\166', '\141', '\040', '\061', '\012', '\161', '\127', '\162', + '\040', '\161', '\165', '\040', '\061', '\012', '\161', '\160', + '\127', '\040', '\161', '\165', '\040', '\061', '\012', '\144', + '\113', '\153', '\040', '\144', '\145', '\040', '\061', '\012', + '\171', '\127', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\152', '\155', '\116', '\040', '\151', '\152', '\040', + '\061', '\012', '\147', '\160', '\126', '\040', '\156', '\147', + '\040', '\061', '\012', '\161', '\172', '\123', '\040', '\161', + '\165', '\040', '\061', '\012', '\157', '\132', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\121', '\155', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\155', '\116', + '\153', '\040', '\155', '\145', '\040', '\061', '\012', '\171', + '\160', '\115', '\040', '\160', '\162', '\040', '\061', '\012', + '\154', '\167', '\110', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\110', '\163', '\040', '\163', '\172', '\040', + '\061', '\012', '\152', '\172', '\103', '\040', '\152', '\157', + '\040', '\061', '\012', '\157', '\112', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\114', '\161', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\150', '\130', '\147', + '\040', '\164', '\150', '\040', '\061', '\012', '\170', '\105', + '\146', '\040', '\146', '\157', '\040', '\061', '\012', '\165', + '\127', '\170', '\040', '\161', '\165', '\040', '\061', '\012', + '\153', '\166', '\124', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\163', '\107', '\040', '\163', '\172', '\040', + '\061', '\012', '\154', '\123', '\170', '\040', '\154', '\145', + '\040', '\061', '\012', '\161', '\113', '\142', '\040', '\161', + '\165', '\040', '\061', '\012', '\121', '\171', '\145', '\040', + '\144', '\145', '\040', '\061', '\012', '\170', '\110', '\153', + '\040', '\153', '\141', '\040', '\061', '\012', '\103', '\167', + '\160', '\040', '\160', '\162', '\040', '\061', '\012', '\172', + '\155', '\112', '\040', '\163', '\172', '\040', '\061', '\012', + '\170', '\165', '\114', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\144', '\110', '\040', '\144', '\145', '\040', + '\061', '\012', '\120', '\142', '\167', '\040', '\167', '\141', + '\040', '\061', '\012', '\161', '\144', '\130', '\040', '\161', + '\165', '\040', '\061', '\012', '\154', '\126', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\142', '\161', '\114', + '\040', '\161', '\165', '\040', '\061', '\012', '\167', '\116', + '\163', '\040', '\163', '\172', '\040', '\061', '\012', '\166', + '\172', '\116', '\040', '\166', '\141', '\040', '\061', '\012', + '\161', '\152', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\150', '\146', '\040', '\164', '\150', '\040', + '\061', '\012', '\171', '\160', '\112', '\040', '\160', '\162', + '\040', '\061', '\012', '\170', '\115', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\142', '\124', '\153', '\040', + '\153', '\141', '\040', '\061', '\012', '\164', '\114', '\146', + '\040', '\164', '\150', '\040', '\061', '\012', '\170', '\147', + '\122', '\040', '\156', '\147', '\040', '\061', '\012', '\153', + '\121', '\172', '\040', '\163', '\172', '\040', '\061', '\012', + '\122', '\152', '\160', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\150', '\107', '\040', '\164', '\150', '\040', + '\061', '\012', '\142', '\103', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\150', '\142', '\106', '\040', '\164', + '\150', '\040', '\061', '\012', '\162', '\170', '\121', '\040', + '\145', '\162', '\040', '\061', '\012', '\161', '\126', '\160', + '\040', '\161', '\165', '\040', '\061', '\012', '\142', '\153', + '\131', '\040', '\153', '\141', '\040', '\061', '\012', '\161', + '\120', '\154', '\040', '\161', '\165', '\040', '\061', '\012', + '\152', '\121', '\153', '\040', '\151', '\152', '\040', '\061', + '\012', '\117', '\166', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\163', '\126', '\166', '\040', '\166', '\141', + '\040', '\061', '\012', '\160', '\155', '\125', '\040', '\155', + '\145', '\040', '\061', '\012', '\165', '\106', '\166', '\040', + '\161', '\165', '\040', '\061', '\012', '\170', '\141', '\132', + '\040', '\166', '\141', '\040', '\061', '\012', '\147', '\107', + '\156', '\040', '\141', '\156', '\040', '\061', '\012', '\160', + '\147', '\111', '\040', '\156', '\147', '\040', '\061', '\012', + '\172', '\124', '\152', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\166', '\103', '\040', '\166', '\141', '\040', + '\061', '\012', '\167', '\107', '\166', '\040', '\166', '\141', + '\040', '\061', '\012', '\162', '\116', '\166', '\040', '\166', + '\141', '\040', '\061', '\012', '\121', '\164', '\161', '\040', + '\164', '\150', '\040', '\061', '\012', '\166', '\116', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\154', '\120', + '\166', '\040', '\166', '\141', '\040', '\061', '\012', '\112', + '\144', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\130', '\144', '\152', '\040', '\144', '\145', '\040', '\061', + '\012', '\171', '\161', '\153', '\040', '\161', '\165', '\040', + '\061', '\012', '\151', '\167', '\131', '\040', '\164', '\151', + '\040', '\061', '\012', '\116', '\155', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\146', '\124', '\160', '\040', + '\160', '\162', '\040', '\061', '\012', '\161', '\172', '\121', + '\040', '\161', '\165', '\040', '\061', '\012', '\160', '\152', + '\101', '\040', '\151', '\152', '\040', '\061', '\012', '\160', + '\166', '\110', '\040', '\166', '\141', '\040', '\061', '\012', + '\170', '\114', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\127', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\166', '\126', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\147', '\121', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\163', '\166', '\131', '\040', + '\166', '\141', '\040', '\061', '\012', '\146', '\114', '\146', + '\040', '\146', '\157', '\040', '\061', '\012', '\161', '\172', + '\102', '\040', '\161', '\165', '\040', '\061', '\012', '\104', + '\170', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\165', '\172', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\126', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\150', '\132', '\142', '\040', '\164', '\150', + '\040', '\061', '\012', '\107', '\160', '\170', '\040', '\160', + '\162', '\040', '\061', '\012', '\170', '\161', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\147', '\143', '\130', + '\040', '\143', '\150', '\040', '\061', '\012', '\110', '\170', + '\144', '\040', '\144', '\145', '\040', '\061', '\012', '\164', + '\125', '\161', '\040', '\164', '\150', '\040', '\061', '\012', + '\142', '\113', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\151', '\107', '\170', '\040', '\164', '\151', '\040', + '\061', '\012', '\170', '\166', '\121', '\040', '\166', '\141', + '\040', '\061', '\012', '\154', '\170', '\101', '\040', '\154', + '\145', '\040', '\061', '\012', '\163', '\152', '\110', '\040', + '\163', '\164', '\040', '\061', '\012', '\107', '\161', '\157', + '\040', '\161', '\165', '\040', '\061', '\012', '\144', '\147', + '\121', '\040', '\144', '\145', '\040', '\061', '\012', '\171', + '\104', '\153', '\040', '\153', '\141', '\040', '\061', '\012', + '\132', '\156', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\166', '\146', '\125', '\040', '\166', '\141', '\040', + '\061', '\012', '\166', '\165', '\104', '\040', '\161', '\165', + '\040', '\061', '\012', '\157', '\121', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\142', '\150', '\104', '\040', + '\164', '\150', '\040', '\061', '\012', '\161', '\114', '\152', + '\040', '\161', '\165', '\040', '\061', '\012', '\155', '\144', + '\131', '\040', '\144', '\145', '\040', '\061', '\012', '\162', + '\132', '\142', '\040', '\145', '\162', '\040', '\061', '\012', + '\153', '\104', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\146', '\163', '\113', '\040', '\163', '\172', '\040', + '\061', '\012', '\113', '\161', '\146', '\040', '\161', '\165', + '\040', '\061', '\012', '\171', '\127', '\154', '\040', '\154', + '\145', '\040', '\061', '\012', '\155', '\126', '\167', '\040', + '\155', '\145', '\040', '\061', '\012', '\155', '\143', '\126', + '\040', '\143', '\150', '\040', '\061', '\012', '\164', '\104', + '\146', '\040', '\164', '\150', '\040', '\061', '\012', '\154', + '\101', '\157', '\040', '\154', '\145', '\040', '\061', '\012', + '\146', '\172', '\122', '\040', '\163', '\172', '\040', '\061', + '\012', '\130', '\162', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\152', '\162', '\132', '\040', '\145', '\162', + '\040', '\061', '\012', '\161', '\155', '\116', '\040', '\161', + '\165', '\040', '\061', '\012', '\112', '\156', '\160', '\040', + '\141', '\156', '\040', '\061', '\012', '\152', '\150', '\103', + '\040', '\164', '\150', '\040', '\061', '\012', '\153', '\161', + '\122', '\040', '\161', '\165', '\040', '\061', '\012', '\144', + '\127', '\156', '\040', '\144', '\145', '\040', '\061', '\012', + '\127', '\155', '\167', '\040', '\155', '\145', '\040', '\061', + '\012', '\122', '\147', '\171', '\040', '\156', '\147', '\040', + '\061', '\012', '\165', '\166', '\116', '\040', '\161', '\165', + '\040', '\061', '\012', '\152', '\151', '\131', '\040', '\164', + '\151', '\040', '\061', '\012', '\170', '\127', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\171', '\112', '\162', + '\040', '\145', '\162', '\040', '\061', '\012', '\157', '\110', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\171', + '\166', '\167', '\040', '\166', '\141', '\040', '\061', '\012', + '\131', '\144', '\156', '\040', '\144', '\145', '\040', '\061', + '\012', '\116', '\166', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\107', '\155', '\166', '\040', '\166', '\141', + '\040', '\061', '\012', '\170', '\170', '\132', '\040', '\170', + '\145', '\040', '\061', '\012', '\130', '\144', '\146', '\040', + '\144', '\145', '\040', '\061', '\012', '\170', '\131', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\126', '\156', + '\166', '\040', '\141', '\156', '\040', '\061', '\012', '\152', + '\116', '\172', '\040', '\163', '\172', '\040', '\061', '\012', + '\127', '\156', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\167', '\153', '\040', '\153', '\141', '\040', + '\061', '\012', '\161', '\127', '\172', '\040', '\161', '\165', + '\040', '\061', '\012', '\155', '\121', '\163', '\040', '\163', + '\172', '\040', '\061', '\012', '\126', '\170', '\142', '\040', + '\142', '\145', '\040', '\061', '\012', '\170', '\167', '\107', + '\040', '\167', '\141', '\040', '\061', '\012', '\167', '\166', + '\160', '\040', '\166', '\141', '\040', '\061', '\012', '\147', + '\155', '\126', '\040', '\156', '\147', '\040', '\061', '\012', + '\122', '\172', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\103', '\160', '\167', '\040', '\160', '\162', '\040', + '\061', '\012', '\107', '\171', '\171', '\040', '\156', '\171', + '\040', '\061', '\012', '\170', '\172', '\101', '\040', '\163', + '\172', '\040', '\061', '\012', '\167', '\107', '\170', '\040', + '\167', '\141', '\040', '\061', '\012', '\142', '\161', '\123', + '\040', '\161', '\165', '\040', '\061', '\012', '\167', '\150', + '\122', '\040', '\164', '\150', '\040', '\061', '\012', '\152', + '\120', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\151', '\161', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\152', '\113', '\040', '\144', '\145', '\040', + '\061', '\012', '\143', '\126', '\153', '\040', '\143', '\150', + '\040', '\061', '\012', '\162', '\167', '\124', '\040', '\145', + '\162', '\040', '\061', '\012', '\126', '\150', '\156', '\040', + '\164', '\150', '\040', '\061', '\012', '\110', '\146', '\167', + '\040', '\167', '\141', '\040', '\061', '\012', '\142', '\156', + '\112', '\040', '\141', '\156', '\040', '\061', '\012', '\103', + '\160', '\144', '\040', '\144', '\145', '\040', '\061', '\012', + '\116', '\155', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\144', '\156', '\117', '\040', '\141', '\156', '\040', + '\061', '\012', '\161', '\127', '\143', '\040', '\161', '\165', + '\040', '\061', '\012', '\141', '\126', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\161', '\117', '\156', '\040', + '\161', '\165', '\040', '\061', '\012', '\121', '\154', '\162', + '\040', '\145', '\162', '\040', '\061', '\012', '\161', '\156', + '\116', '\040', '\161', '\165', '\040', '\061', '\012', '\162', + '\114', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\167', '\164', '\105', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\147', '\122', '\040', '\156', '\147', '\040', + '\061', '\012', '\131', '\161', '\160', '\040', '\161', '\165', + '\040', '\061', '\012', '\110', '\167', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\156', '\127', '\153', '\040', + '\141', '\156', '\040', '\061', '\012', '\167', '\161', '\102', + '\040', '\161', '\165', '\040', '\061', '\012', '\146', '\101', + '\160', '\040', '\160', '\162', '\040', '\061', '\012', '\150', + '\132', '\166', '\040', '\164', '\150', '\040', '\061', '\012', + '\113', '\172', '\160', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\116', '\153', '\040', '\153', '\141', '\040', + '\061', '\012', '\124', '\153', '\144', '\040', '\144', '\145', + '\040', '\061', '\012', '\165', '\131', '\155', '\040', '\161', + '\165', '\040', '\061', '\012', '\153', '\143', '\122', '\040', + '\143', '\150', '\040', '\061', '\012', '\170', '\116', '\154', + '\040', '\154', '\145', '\040', '\061', '\012', '\153', '\110', + '\153', '\040', '\153', '\141', '\040', '\061', '\012', '\142', + '\112', '\153', '\040', '\153', '\141', '\040', '\061', '\012', + '\152', '\152', '\104', '\040', '\151', '\152', '\040', '\061', + '\012', '\116', '\154', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\144', '\150', '\102', '\040', '\164', '\150', + '\040', '\061', '\012', '\152', '\130', '\154', '\040', '\154', + '\145', '\040', '\061', '\012', '\156', '\167', '\102', '\040', + '\141', '\156', '\040', '\061', '\012', '\110', '\172', '\142', + '\040', '\163', '\172', '\040', '\061', '\012', '\161', '\121', + '\172', '\040', '\161', '\165', '\040', '\061', '\012', '\146', + '\113', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\152', '\126', '\167', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\154', '\125', '\040', '\154', '\145', '\040', + '\061', '\012', '\114', '\172', '\152', '\040', '\163', '\172', + '\040', '\061', '\012', '\163', '\130', '\165', '\040', '\161', + '\165', '\040', '\061', '\012', '\167', '\102', '\167', '\040', + '\167', '\141', '\040', '\061', '\012', '\111', '\161', '\147', + '\040', '\161', '\165', '\040', '\061', '\012', '\167', '\152', + '\126', '\040', '\151', '\152', '\040', '\061', '\012', '\167', + '\170', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\152', '\172', '\113', '\040', '\163', '\172', '\040', '\061', + '\012', '\162', '\104', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\165', '\121', '\171', '\040', '\161', '\165', + '\040', '\061', '\012', '\161', '\107', '\167', '\040', '\161', + '\165', '\040', '\061', '\012', '\164', '\142', '\125', '\040', + '\164', '\150', '\040', '\061', '\012', '\153', '\125', '\157', + '\040', '\153', '\141', '\040', '\061', '\012', '\144', '\126', + '\155', '\040', '\144', '\145', '\040', '\061', '\012', '\104', + '\144', '\156', '\040', '\141', '\156', '\040', '\061', '\012', + '\166', '\161', '\103', '\040', '\166', '\157', '\040', '\061', + '\012', '\152', '\153', '\132', '\040', '\151', '\152', '\040', + '\061', '\012', '\114', '\166', '\172', '\040', '\166', '\141', + '\040', '\061', '\012', '\164', '\120', '\171', '\040', '\164', + '\150', '\040', '\061', '\012', '\126', '\146', '\152', '\040', + '\151', '\152', '\040', '\061', '\012', '\121', '\150', '\142', + '\040', '\164', '\150', '\040', '\061', '\012', '\167', '\150', + '\102', '\040', '\164', '\150', '\040', '\061', '\012', '\106', + '\161', '\146', '\040', '\161', '\165', '\040', '\061', '\012', + '\150', '\103', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\106', '\152', '\146', '\040', '\151', '\152', '\040', + '\061', '\012', '\121', '\146', '\162', '\040', '\145', '\162', + '\040', '\061', '\012', '\172', '\167', '\106', '\040', '\163', + '\172', '\040', '\061', '\012', '\106', '\167', '\146', '\040', + '\167', '\141', '\040', '\061', '\012', '\160', '\166', '\125', + '\040', '\166', '\141', '\040', '\061', '\012', '\167', '\150', + '\103', '\040', '\164', '\150', '\040', '\061', '\012', '\150', + '\124', '\153', '\040', '\164', '\150', '\040', '\061', '\012', + '\144', '\154', '\121', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\172', '\114', '\040', '\163', '\172', '\040', + '\061', '\012', '\172', '\161', '\123', '\040', '\161', '\165', + '\040', '\061', '\012', '\161', '\164', '\120', '\040', '\164', + '\150', '\040', '\061', '\012', '\171', '\150', '\103', '\040', + '\164', '\150', '\040', '\061', '\012', '\171', '\152', '\102', + '\040', '\151', '\152', '\040', '\061', '\012', '\151', '\124', + '\144', '\040', '\144', '\145', '\040', '\061', '\012', '\153', + '\114', '\170', '\040', '\153', '\141', '\040', '\061', '\012', + '\122', '\161', '\151', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\152', '\123', '\040', '\161', '\165', '\040', + '\061', '\012', '\166', '\152', '\111', '\040', '\166', '\141', + '\040', '\061', '\012', '\160', '\107', '\172', '\040', '\163', + '\172', '\040', '\061', '\012', '\167', '\156', '\126', '\040', + '\141', '\156', '\040', '\061', '\012', '\154', '\121', '\170', + '\040', '\154', '\145', '\040', '\061', '\012', '\165', '\166', + '\123', '\040', '\161', '\165', '\040', '\061', '\012', '\132', + '\147', '\145', '\040', '\144', '\145', '\040', '\061', '\012', + '\147', '\112', '\166', '\040', '\156', '\147', '\040', '\061', + '\012', '\131', '\144', '\142', '\040', '\144', '\145', '\040', + '\061', '\012', '\167', '\104', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\172', '\167', '\126', '\040', '\163', + '\172', '\040', '\061', '\012', '\150', '\116', '\155', '\040', + '\164', '\150', '\040', '\061', '\012', '\172', '\167', '\121', + '\040', '\163', '\172', '\040', '\061', '\012', '\146', '\122', + '\162', '\040', '\145', '\162', '\040', '\061', '\012', '\167', + '\126', '\162', '\040', '\145', '\162', '\040', '\061', '\012', + '\156', '\113', '\147', '\040', '\141', '\156', '\040', '\061', + '\012', '\124', '\147', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\142', '\131', '\160', '\040', '\160', '\162', + '\040', '\061', '\012', '\154', '\102', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\172', '\152', '\160', '\040', + '\163', '\172', '\040', '\061', '\012', '\161', '\101', '\146', + '\040', '\161', '\165', '\040', '\061', '\012', '\172', '\155', + '\113', '\040', '\155', '\145', '\040', '\061', '\012', '\167', + '\161', '\113', '\040', '\161', '\165', '\040', '\061', '\012', + '\166', '\152', '\124', '\040', '\166', '\141', '\040', '\061', + '\012', '\114', '\161', '\154', '\040', '\161', '\165', '\040', + '\061', '\012', '\163', '\156', '\103', '\040', '\141', '\156', + '\040', '\061', '\012', '\146', '\172', '\131', '\040', '\163', + '\172', '\040', '\061', '\012', '\166', '\161', '\125', '\040', + '\161', '\165', '\040', '\061', '\012', '\155', '\107', '\142', + '\040', '\155', '\145', '\040', '\061', '\012', '\146', '\153', + '\120', '\040', '\153', '\141', '\040', '\061', '\012', '\167', + '\121', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\106', '\161', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\126', '\155', '\040', '\155', '\145', '\040', + '\061', '\012', '\127', '\143', '\170', '\040', '\143', '\150', + '\040', '\061', '\012', '\167', '\160', '\131', '\040', '\167', + '\141', '\040', '\061', '\012', '\154', '\106', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\147', '\167', '\104', + '\040', '\156', '\147', '\040', '\061', '\012', '\147', '\127', + '\160', '\040', '\156', '\147', '\040', '\061', '\012', '\146', + '\152', '\124', '\040', '\151', '\152', '\040', '\061', '\012', + '\160', '\106', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\111', '\160', '\040', '\151', '\156', '\040', + '\061', '\012', '\164', '\142', '\104', '\040', '\164', '\150', + '\040', '\061', '\012', '\130', '\161', '\143', '\040', '\161', + '\165', '\040', '\061', '\012', '\121', '\153', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\161', '\145', '\132', + '\040', '\161', '\165', '\040', '\061', '\012', '\161', '\120', + '\142', '\040', '\161', '\165', '\040', '\061', '\012', '\147', + '\167', '\114', '\040', '\156', '\147', '\040', '\061', '\012', + '\146', '\110', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\170', '\167', '\120', '\040', '\167', '\141', '\040', + '\061', '\012', '\170', '\166', '\102', '\040', '\166', '\141', + '\040', '\061', '\012', '\152', '\123', '\167', '\040', '\151', + '\152', '\040', '\061', '\012', '\160', '\172', '\106', '\040', + '\163', '\172', '\040', '\061', '\012', '\167', '\131', '\160', + '\040', '\167', '\141', '\040', '\061', '\012', '\144', '\104', + '\170', '\040', '\144', '\145', '\040', '\061', '\012', '\156', + '\102', '\170', '\040', '\141', '\156', '\040', '\061', '\012', + '\143', '\116', '\166', '\040', '\143', '\150', '\040', '\061', + '\012', '\125', '\142', '\155', '\040', '\155', '\145', '\040', + '\061', '\012', '\170', '\130', '\165', '\040', '\161', '\165', + '\040', '\061', '\012', '\144', '\122', '\154', '\040', '\144', + '\145', '\040', '\061', '\012', '\144', '\102', '\172', '\040', + '\144', '\145', '\040', '\061', '\012', '\130', '\166', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\130', '\154', + '\144', '\040', '\144', '\145', '\040', '\061', '\012', '\155', + '\167', '\131', '\040', '\155', '\145', '\040', '\061', '\012', + '\167', '\150', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\115', '\172', '\154', '\040', '\154', '\145', '\040', + '\061', '\012', '\101', '\161', '\152', '\040', '\161', '\165', + '\040', '\061', '\012', '\165', '\104', '\160', '\040', '\161', + '\165', '\040', '\061', '\012', '\143', '\152', '\132', '\040', + '\143', '\150', '\040', '\061', '\012', '\126', '\153', '\146', + '\040', '\153', '\141', '\040', '\061', '\012', '\165', '\107', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\150', + '\102', '\163', '\040', '\164', '\150', '\040', '\061', '\012', + '\161', '\114', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\146', '\127', '\040', '\164', '\150', '\040', + '\061', '\012', '\143', '\120', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\170', '\157', '\116', '\040', '\157', + '\156', '\040', '\061', '\012', '\131', '\144', '\170', '\040', + '\144', '\145', '\040', '\061', '\012', '\114', '\170', '\153', + '\040', '\153', '\141', '\040', '\061', '\012', '\143', '\143', + '\132', '\040', '\143', '\150', '\040', '\061', '\012', '\165', + '\112', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\163', '\126', '\160', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\162', '\105', '\040', '\145', '\162', '\040', + '\061', '\012', '\170', '\147', '\120', '\040', '\156', '\147', + '\040', '\061', '\012', '\150', '\120', '\160', '\040', '\164', + '\150', '\040', '\061', '\012', '\145', '\165', '\125', '\040', + '\161', '\165', '\040', '\061', '\012', '\163', '\132', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\161', '\156', + '\113', '\040', '\161', '\165', '\040', '\061', '\012', '\102', + '\147', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\163', '\154', '\121', '\040', '\154', '\145', '\040', '\061', + '\012', '\147', '\170', '\101', '\040', '\156', '\147', '\040', + '\061', '\012', '\152', '\114', '\144', '\040', '\144', '\145', + '\040', '\061', '\012', '\172', '\156', '\104', '\040', '\141', + '\156', '\040', '\061', '\012', '\153', '\130', '\153', '\040', + '\153', '\141', '\040', '\061', '\012', '\164', '\146', '\126', + '\040', '\164', '\150', '\040', '\061', '\012', '\126', '\167', + '\154', '\040', '\154', '\145', '\040', '\061', '\012', '\170', + '\127', '\144', '\040', '\144', '\157', '\040', '\061', '\012', + '\170', '\156', '\110', '\040', '\141', '\156', '\040', '\061', + '\012', '\143', '\117', '\161', '\040', '\143', '\150', '\040', + '\061', '\012', '\114', '\153', '\153', '\040', '\153', '\141', + '\040', '\061', '\012', '\116', '\166', '\171', '\040', '\166', + '\141', '\040', '\061', '\012', '\170', '\111', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\170', '\153', '\113', + '\040', '\153', '\141', '\040', '\061', '\012', '\162', '\115', + '\162', '\040', '\145', '\162', '\040', '\061', '\012', '\162', + '\155', '\121', '\040', '\145', '\162', '\040', '\061', '\012', + '\142', '\120', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\101', '\141', '\040', '\141', '\156', '\040', + '\061', '\012', '\166', '\121', '\166', '\040', '\166', '\141', + '\040', '\061', '\012', '\146', '\110', '\162', '\040', '\145', + '\162', '\040', '\061', '\012', '\120', '\155', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\166', '\172', '\112', + '\040', '\163', '\172', '\040', '\061', '\012', '\167', '\124', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\142', + '\127', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\132', '\167', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\113', '\170', '\040', '\156', '\147', '\040', + '\061', '\012', '\107', '\142', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\167', '\115', '\153', '\040', '\153', + '\141', '\040', '\061', '\012', '\116', '\146', '\170', '\040', + '\146', '\157', '\040', '\061', '\012', '\146', '\101', '\157', + '\040', '\157', '\156', '\040', '\061', '\012', '\144', '\110', + '\142', '\040', '\144', '\145', '\040', '\061', '\012', '\154', + '\170', '\110', '\040', '\154', '\145', '\040', '\061', '\012', + '\144', '\161', '\117', '\040', '\161', '\165', '\040', '\061', + '\012', '\124', '\154', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\131', '\152', '\152', '\040', '\151', '\152', + '\040', '\061', '\012', '\111', '\171', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\165', '\157', '\131', '\040', + '\161', '\165', '\040', '\061', '\012', '\155', '\150', '\110', + '\040', '\164', '\150', '\040', '\061', '\012', '\154', '\115', + '\152', '\040', '\154', '\145', '\040', '\061', '\012', '\146', + '\172', '\106', '\040', '\163', '\172', '\040', '\061', '\012', + '\146', '\162', '\122', '\040', '\145', '\162', '\040', '\061', + '\012', '\171', '\116', '\154', '\040', '\154', '\145', '\040', + '\061', '\012', '\141', '\120', '\166', '\040', '\141', '\156', + '\040', '\061', '\012', '\171', '\167', '\107', '\040', '\167', + '\141', '\040', '\061', '\012', '\103', '\155', '\167', '\040', + '\155', '\145', '\040', '\061', '\012', '\163', '\166', '\113', + '\040', '\166', '\141', '\040', '\061', '\012', '\163', '\162', + '\117', '\040', '\145', '\162', '\040', '\061', '\012', '\125', + '\150', '\172', '\040', '\164', '\150', '\040', '\061', '\012', + '\166', '\120', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\124', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\153', '\172', '\110', '\040', '\163', '\172', + '\040', '\061', '\012', '\111', '\157', '\170', '\040', '\157', + '\156', '\040', '\061', '\012', '\146', '\121', '\141', '\040', + '\141', '\156', '\040', '\061', '\012', '\167', '\132', '\162', + '\040', '\145', '\162', '\040', '\061', '\012', '\156', '\161', + '\125', '\040', '\141', '\156', '\040', '\061', '\012', '\167', + '\120', '\142', '\040', '\167', '\141', '\040', '\061', '\012', + '\124', '\172', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\156', '\122', '\040', '\141', '\156', '\040', + '\061', '\012', '\166', '\146', '\112', '\040', '\166', '\141', + '\040', '\061', '\012', '\166', '\171', '\130', '\040', '\166', + '\141', '\040', '\061', '\012', '\146', '\114', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\172', '\152', '\120', + '\040', '\163', '\172', '\040', '\061', '\012', '\160', '\155', + '\122', '\040', '\155', '\145', '\040', '\061', '\012', '\145', + '\120', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\152', '\171', '\124', '\040', '\151', '\152', '\040', '\061', + '\012', '\155', '\152', '\120', '\040', '\151', '\152', '\040', + '\061', '\012', '\146', '\163', '\110', '\040', '\163', '\172', + '\040', '\061', '\012', '\166', '\167', '\102', '\040', '\166', + '\141', '\040', '\061', '\012', '\131', '\156', '\162', '\040', + '\141', '\156', '\040', '\061', '\012', '\124', '\161', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\114', '\166', + '\166', '\040', '\166', '\141', '\040', '\061', '\012', '\164', + '\103', '\146', '\040', '\164', '\150', '\040', '\061', '\012', + '\167', '\160', '\102', '\040', '\167', '\141', '\040', '\061', + '\012', '\167', '\130', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\155', '\150', '\130', '\040', '\164', '\150', + '\040', '\061', '\012', '\153', '\131', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\104', '\160', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\171', '\147', '\122', + '\040', '\156', '\147', '\040', '\061', '\012', '\122', '\146', + '\160', '\040', '\160', '\162', '\040', '\061', '\012', '\112', + '\171', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\171', '\170', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\120', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\141', '\117', '\152', '\040', '\141', '\156', + '\040', '\061', '\012', '\132', '\167', '\167', '\040', '\167', + '\141', '\040', '\061', '\012', '\146', '\106', '\170', '\040', + '\146', '\157', '\040', '\061', '\012', '\142', '\104', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\161', '\113', + '\170', '\040', '\161', '\165', '\040', '\061', '\012', '\167', + '\110', '\170', '\040', '\167', '\141', '\040', '\061', '\012', + '\150', '\162', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\106', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\154', '\114', '\170', '\040', '\154', '\145', + '\040', '\061', '\012', '\141', '\131', '\152', '\040', '\141', + '\156', '\040', '\061', '\012', '\153', '\103', '\163', '\040', + '\163', '\172', '\040', '\061', '\012', '\154', '\127', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\160', '\144', + '\131', '\040', '\144', '\145', '\040', '\061', '\012', '\163', + '\167', '\111', '\040', '\163', '\172', '\040', '\061', '\012', + '\142', '\114', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\115', '\172', '\170', '\040', '\163', '\172', '\040', + '\061', '\012', '\143', '\113', '\153', '\040', '\143', '\150', + '\040', '\061', '\012', '\150', '\115', '\172', '\040', '\164', + '\150', '\040', '\061', '\012', '\112', '\143', '\165', '\040', + '\161', '\165', '\040', '\061', '\012', '\167', '\152', '\102', + '\040', '\151', '\152', '\040', '\061', '\012', '\115', '\161', + '\145', '\040', '\161', '\165', '\040', '\061', '\012', '\162', + '\170', '\127', '\040', '\145', '\162', '\040', '\061', '\012', + '\147', '\132', '\166', '\040', '\156', '\147', '\040', '\061', + '\012', '\122', '\146', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\160', '\167', '\104', '\040', '\167', '\141', + '\040', '\061', '\012', '\154', '\150', '\130', '\040', '\164', + '\150', '\040', '\061', '\012', '\146', '\126', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\166', '\146', '\127', + '\040', '\166', '\141', '\040', '\061', '\012', '\154', '\170', + '\120', '\040', '\154', '\145', '\040', '\061', '\012', '\131', + '\171', '\152', '\040', '\151', '\152', '\040', '\061', '\012', + '\150', '\120', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\125', '\170', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\142', '\144', '\117', '\040', '\144', '\145', + '\040', '\061', '\012', '\142', '\122', '\172', '\040', '\163', + '\172', '\040', '\061', '\012', '\144', '\130', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\122', '\152', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\146', '\147', + '\126', '\040', '\156', '\147', '\040', '\061', '\012', '\170', + '\101', '\146', '\040', '\146', '\157', '\040', '\061', '\012', + '\167', '\130', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\113', '\166', '\166', '\040', '\166', '\141', '\040', + '\061', '\012', '\163', '\166', '\114', '\040', '\166', '\141', + '\040', '\061', '\012', '\146', '\127', '\166', '\040', '\166', + '\141', '\040', '\061', '\012', '\144', '\162', '\121', '\040', + '\145', '\162', '\040', '\061', '\012', '\114', '\160', '\166', + '\040', '\166', '\141', '\040', '\061', '\012', '\161', '\113', + '\160', '\040', '\161', '\165', '\040', '\061', '\012', '\145', + '\103', '\166', '\040', '\145', '\162', '\040', '\061', '\012', + '\170', '\167', '\110', '\040', '\167', '\141', '\040', '\061', + '\012', '\143', '\166', '\103', '\040', '\143', '\150', '\040', + '\061', '\012', '\153', '\125', '\146', '\040', '\153', '\141', + '\040', '\061', '\012', '\157', '\120', '\170', '\040', '\157', + '\156', '\040', '\061', '\012', '\164', '\152', '\112', '\040', + '\164', '\150', '\040', '\061', '\012', '\142', '\102', '\153', + '\040', '\153', '\141', '\040', '\061', '\012', '\166', '\160', + '\111', '\040', '\166', '\141', '\040', '\061', '\012', '\147', + '\172', '\131', '\040', '\156', '\147', '\040', '\061', '\012', + '\157', '\132', '\163', '\040', '\157', '\156', '\040', '\061', + '\012', '\160', '\113', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\170', '\113', '\163', '\040', '\163', '\172', + '\040', '\061', '\012', '\161', '\143', '\110', '\040', '\161', + '\165', '\040', '\061', '\012', '\126', '\146', '\155', '\040', + '\155', '\145', '\040', '\061', '\012', '\163', '\166', '\115', + '\040', '\166', '\141', '\040', '\061', '\012', '\126', '\152', + '\170', '\040', '\151', '\152', '\040', '\061', '\012', '\154', + '\126', '\167', '\040', '\154', '\145', '\040', '\061', '\012', + '\167', '\127', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\130', '\160', '\170', '\040', '\160', '\162', '\040', + '\061', '\012', '\154', '\143', '\101', '\040', '\143', '\150', + '\040', '\061', '\012', '\164', '\114', '\143', '\040', '\164', + '\150', '\040', '\061', '\012', '\154', '\104', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\130', '\152', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\130', '\144', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\162', + '\113', '\155', '\040', '\145', '\162', '\040', '\061', '\012', + '\146', '\156', '\127', '\040', '\141', '\156', '\040', '\061', + '\012', '\124', '\143', '\142', '\040', '\143', '\150', '\040', + '\061', '\012', '\161', '\147', '\130', '\040', '\161', '\165', + '\040', '\061', '\012', '\161', '\132', '\157', '\040', '\161', + '\165', '\040', '\061', '\012', '\145', '\112', '\166', '\040', + '\145', '\162', '\040', '\061', '\012', '\131', '\170', '\171', + '\040', '\156', '\171', '\040', '\061', '\012', '\153', '\146', + '\115', '\040', '\153', '\141', '\040', '\061', '\012', '\161', + '\113', '\145', '\040', '\161', '\165', '\040', '\061', '\012', + '\166', '\115', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\144', '\147', '\131', '\040', '\144', '\145', '\040', + '\061', '\012', '\147', '\107', '\144', '\040', '\156', '\147', + '\040', '\061', '\012', '\126', '\143', '\152', '\040', '\143', + '\150', '\040', '\061', '\012', '\123', '\146', '\167', '\040', + '\167', '\141', '\040', '\061', '\012', '\170', '\104', '\153', + '\040', '\153', '\141', '\040', '\061', '\012', '\146', '\124', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\161', + '\122', '\167', '\040', '\161', '\165', '\040', '\061', '\012', + '\164', '\117', '\141', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\165', '\121', '\040', '\161', '\165', '\040', + '\061', '\012', '\155', '\147', '\112', '\040', '\156', '\147', + '\040', '\061', '\012', '\142', '\122', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\153', '\131', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\170', '\167', '\104', + '\040', '\167', '\141', '\040', '\061', '\012', '\166', '\130', + '\163', '\040', '\166', '\141', '\040', '\061', '\012', '\172', + '\154', '\103', '\040', '\154', '\145', '\040', '\061', '\012', + '\153', '\155', '\110', '\040', '\153', '\141', '\040', '\061', + '\012', '\152', '\150', '\132', '\040', '\164', '\150', '\040', + '\061', '\012', '\127', '\170', '\157', '\040', '\157', '\156', + '\040', '\061', '\012', '\166', '\164', '\130', '\040', '\164', + '\150', '\040', '\061', '\012', '\151', '\127', '\155', '\040', + '\151', '\156', '\040', '\061', '\012', '\161', '\126', '\170', + '\040', '\161', '\165', '\040', '\061', '\012', '\110', '\152', + '\166', '\040', '\166', '\141', '\040', '\061', '\012', '\120', + '\170', '\163', '\040', '\163', '\172', '\040', '\061', '\012', + '\142', '\131', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\167', '\147', '\107', '\040', '\156', '\147', '\040', + '\061', '\012', '\112', '\166', '\163', '\040', '\166', '\141', + '\040', '\061', '\012', '\147', '\110', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\113', '\172', '\171', '\040', + '\163', '\172', '\040', '\061', '\012', '\170', '\152', '\111', + '\040', '\151', '\152', '\040', '\061', '\012', '\165', '\126', + '\142', '\040', '\161', '\165', '\040', '\061', '\012', '\120', + '\172', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\150', '\170', '\103', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\120', '\171', '\040', '\167', '\141', '\040', + '\061', '\012', '\142', '\130', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\152', '\172', '\131', '\040', '\163', + '\172', '\040', '\061', '\012', '\146', '\161', '\112', '\040', + '\161', '\165', '\040', '\061', '\012', '\161', '\170', '\130', + '\040', '\161', '\165', '\040', '\061', '\012', '\166', '\146', + '\102', '\040', '\166', '\141', '\040', '\061', '\012', '\160', + '\120', '\155', '\040', '\155', '\145', '\040', '\061', '\012', + '\142', '\160', '\103', '\040', '\160', '\162', '\040', '\061', + '\012', '\150', '\106', '\166', '\040', '\164', '\150', '\040', + '\061', '\012', '\103', '\161', '\154', '\040', '\161', '\165', + '\040', '\061', '\012', '\144', '\167', '\111', '\040', '\144', + '\145', '\040', '\061', '\012', '\124', '\143', '\161', '\040', + '\143', '\150', '\040', '\061', '\012', '\132', '\152', '\170', + '\040', '\151', '\152', '\040', '\061', '\012', '\167', '\117', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\112', + '\146', '\152', '\040', '\151', '\152', '\040', '\061', '\012', + '\151', '\132', '\162', '\040', '\151', '\156', '\040', '\061', + '\012', '\126', '\170', '\146', '\040', '\146', '\157', '\040', + '\061', '\012', '\114', '\160', '\170', '\040', '\160', '\162', + '\040', '\061', '\012', '\146', '\110', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\150', '\106', '\171', '\040', + '\164', '\150', '\040', '\061', '\012', '\154', '\143', '\104', + '\040', '\143', '\150', '\040', '\061', '\012', '\166', '\115', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\170', + '\171', '\125', '\040', '\156', '\171', '\040', '\061', '\012', + '\155', '\107', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\112', '\166', '\040', '\166', '\141', '\040', + '\061', '\012', '\172', '\113', '\163', '\040', '\163', '\172', + '\040', '\061', '\012', '\154', '\115', '\155', '\040', '\154', + '\145', '\040', '\061', '\012', '\155', '\161', '\125', '\040', + '\161', '\165', '\040', '\061', '\012', '\166', '\110', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\154', '\107', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\145', + '\111', '\152', '\040', '\164', '\145', '\040', '\061', '\012', + '\126', '\144', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\103', '\153', '\040', '\145', '\162', '\040', + '\061', '\012', '\167', '\121', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\131', '\167', '\146', '\040', '\167', + '\141', '\040', '\061', '\012', '\172', '\125', '\146', '\040', + '\163', '\172', '\040', '\061', '\012', '\161', '\132', '\163', + '\040', '\161', '\165', '\040', '\061', '\012', '\166', '\116', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\104', + '\170', '\152', '\040', '\151', '\152', '\040', '\061', '\012', + '\143', '\131', '\162', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\113', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\166', '\104', '\160', '\040', '\166', '\141', + '\040', '\061', '\012', '\161', '\156', '\106', '\040', '\141', + '\156', '\040', '\061', '\012', '\114', '\163', '\152', '\040', + '\163', '\172', '\040', '\061', '\012', '\170', '\110', '\166', + '\040', '\166', '\141', '\040', '\061', '\012', '\152', '\103', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\142', + '\156', '\130', '\040', '\141', '\156', '\040', '\061', '\012', + '\146', '\102', '\170', '\040', '\146', '\157', '\040', '\061', + '\012', '\152', '\126', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\161', '\117', '\171', '\040', '\161', '\165', + '\040', '\061', '\012', '\165', '\161', '\104', '\040', '\161', + '\165', '\040', '\061', '\012', '\122', '\146', '\167', '\040', + '\167', '\141', '\040', '\061', '\012', '\143', '\152', '\123', + '\040', '\143', '\150', '\040', '\061', '\012', '\165', '\146', + '\130', '\040', '\161', '\165', '\040', '\061', '\012', '\146', + '\166', '\111', '\040', '\166', '\141', '\040', '\061', '\012', + '\117', '\167', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\147', '\130', '\167', '\040', '\156', '\147', '\040', + '\061', '\012', '\157', '\103', '\166', '\040', '\166', '\141', + '\040', '\061', '\012', '\115', '\162', '\170', '\040', '\145', + '\162', '\040', '\061', '\012', '\143', '\111', '\142', '\040', + '\143', '\150', '\040', '\061', '\012', '\146', '\112', '\152', + '\040', '\151', '\152', '\040', '\061', '\012', '\153', '\161', + '\115', '\040', '\161', '\165', '\040', '\061', '\012', '\172', + '\161', '\114', '\040', '\161', '\165', '\040', '\061', '\012', + '\162', '\120', '\172', '\040', '\145', '\162', '\040', '\061', + '\012', '\151', '\167', '\127', '\040', '\151', '\156', '\040', + '\061', '\012', '\143', '\115', '\160', '\040', '\143', '\150', + '\040', '\061', '\012', '\154', '\126', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\166', '\124', '\142', '\040', + '\166', '\141', '\040', '\061', '\012', '\111', '\167', '\146', + '\040', '\167', '\141', '\040', '\061', '\012', '\170', '\154', + '\132', '\040', '\154', '\145', '\040', '\061', '\012', '\166', + '\152', '\121', '\040', '\166', '\141', '\040', '\061', '\012', + '\151', '\120', '\142', '\040', '\151', '\156', '\040', '\061', + '\012', '\127', '\150', '\153', '\040', '\164', '\150', '\040', + '\061', '\012', '\127', '\166', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\155', '\172', '\104', '\040', '\163', + '\172', '\040', '\061', '\012', '\110', '\161', '\153', '\040', + '\161', '\165', '\040', '\061', '\012', '\152', '\161', '\102', + '\040', '\161', '\165', '\040', '\061', '\012', '\161', '\150', + '\115', '\040', '\164', '\150', '\040', '\061', '\012', '\160', + '\162', '\122', '\040', '\145', '\162', '\040', '\061', '\012', + '\156', '\154', '\126', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\131', '\153', '\040', '\161', '\165', '\040', + '\061', '\012', '\172', '\126', '\160', '\040', '\163', '\172', + '\040', '\061', '\012', '\166', '\160', '\117', '\040', '\166', + '\141', '\040', '\061', '\012', '\122', '\166', '\162', '\040', + '\145', '\162', '\040', '\061', '\012', '\163', '\143', '\131', + '\040', '\143', '\150', '\040', '\061', '\012', '\161', '\144', + '\101', '\040', '\161', '\165', '\040', '\061', '\012', '\166', + '\114', '\153', '\040', '\166', '\141', '\040', '\061', '\012', + '\163', '\166', '\111', '\040', '\166', '\141', '\040', '\061', + '\012', '\155', '\144', '\105', '\040', '\144', '\145', '\040', + '\061', '\012', '\150', '\102', '\170', '\040', '\164', '\150', + '\040', '\061', '\012', '\132', '\162', '\166', '\040', '\145', + '\162', '\040', '\061', '\012', '\152', '\127', '\164', '\040', + '\164', '\150', '\040', '\061', '\012', '\146', '\124', '\170', + '\040', '\146', '\157', '\040', '\061', '\012', '\131', '\160', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\155', + '\115', '\153', '\040', '\153', '\141', '\040', '\061', '\012', + '\146', '\144', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\143', '\113', '\040', '\164', '\150', '\040', + '\061', '\012', '\170', '\103', '\171', '\040', '\156', '\171', + '\040', '\061', '\012', '\146', '\126', '\162', '\040', '\145', + '\162', '\040', '\061', '\012', '\141', '\120', '\170', '\040', + '\141', '\156', '\040', '\061', '\012', '\146', '\160', '\125', + '\040', '\160', '\162', '\040', '\061', '\012', '\126', '\153', + '\142', '\040', '\153', '\141', '\040', '\061', '\012', '\164', + '\142', '\115', '\040', '\164', '\150', '\040', '\061', '\012', + '\172', '\121', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\170', '\126', '\040', '\156', '\147', '\040', + '\061', '\012', '\123', '\146', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\160', '\131', '\154', '\040', '\154', + '\145', '\040', '\061', '\012', '\147', '\127', '\164', '\040', + '\164', '\150', '\040', '\061', '\012', '\170', '\105', '\142', + '\040', '\142', '\145', '\040', '\061', '\012', '\155', '\130', + '\171', '\040', '\155', '\145', '\040', '\061', '\012', '\154', + '\156', '\121', '\040', '\141', '\156', '\040', '\061', '\012', + '\161', '\155', '\114', '\040', '\161', '\165', '\040', '\061', + '\012', '\126', '\153', '\171', '\040', '\153', '\141', '\040', + '\061', '\012', '\167', '\167', '\130', '\040', '\167', '\141', + '\040', '\061', '\012', '\125', '\167', '\170', '\040', '\167', + '\141', '\040', '\061', '\012', '\143', '\146', '\102', '\040', + '\143', '\150', '\040', '\061', '\012', '\107', '\170', '\160', + '\040', '\160', '\162', '\040', '\061', '\012', '\146', '\160', + '\114', '\040', '\160', '\162', '\040', '\061', '\012', '\152', + '\124', '\170', '\040', '\151', '\152', '\040', '\061', '\012', + '\143', '\132', '\166', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\154', '\113', '\040', '\154', '\145', '\040', + '\061', '\012', '\150', '\102', '\143', '\040', '\164', '\150', + '\040', '\061', '\012', '\127', '\161', '\151', '\040', '\161', + '\165', '\040', '\061', '\012', '\154', '\107', '\163', '\040', + '\154', '\145', '\040', '\061', '\012', '\104', '\161', '\172', + '\040', '\161', '\165', '\040', '\061', '\012', '\112', '\147', + '\167', '\040', '\156', '\147', '\040', '\061', '\012', '\147', + '\103', '\170', '\040', '\156', '\147', '\040', '\061', '\012', + '\143', '\116', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\161', '\112', '\040', '\143', '\150', '\040', + '\061', '\012', '\142', '\154', '\104', '\040', '\154', '\145', + '\040', '\061', '\012', '\161', '\130', '\162', '\040', '\161', + '\165', '\040', '\061', '\012', '\153', '\130', '\162', '\040', + '\145', '\162', '\040', '\061', '\012', '\153', '\150', '\113', + '\040', '\164', '\150', '\040', '\061', '\012', '\170', '\132', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\152', + '\123', '\163', '\040', '\163', '\172', '\040', '\061', '\012', + '\171', '\152', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\110', '\167', '\146', '\040', '\167', '\141', '\040', + '\061', '\012', '\146', '\130', '\163', '\040', '\163', '\172', + '\040', '\061', '\012', '\161', '\147', '\172', '\040', '\161', + '\165', '\040', '\061', '\012', '\130', '\144', '\167', '\040', + '\144', '\145', '\040', '\061', '\012', '\150', '\143', '\116', + '\040', '\164', '\150', '\040', '\061', '\012', '\152', '\112', + '\144', '\040', '\144', '\145', '\040', '\061', '\012', '\143', + '\155', '\121', '\040', '\143', '\150', '\040', '\061', '\012', + '\155', '\166', '\126', '\040', '\166', '\141', '\040', '\061', + '\012', '\116', '\161', '\145', '\040', '\161', '\165', '\040', + '\061', '\012', '\172', '\170', '\123', '\040', '\163', '\172', + '\040', '\061', '\012', '\153', '\107', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\164', '\106', '\147', '\040', + '\164', '\150', '\040', '\061', '\012', '\146', '\172', '\115', + '\040', '\163', '\172', '\040', '\061', '\012', '\130', '\162', + '\162', '\040', '\145', '\162', '\040', '\061', '\012', '\144', + '\143', '\112', '\040', '\143', '\150', '\040', '\061', '\012', + '\144', '\121', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\116', '\171', '\040', '\161', '\165', '\040', + '\061', '\012', '\150', '\170', '\124', '\040', '\164', '\150', + '\040', '\061', '\012', '\164', '\167', '\102', '\040', '\164', + '\150', '\040', '\061', '\012', '\102', '\161', '\152', '\040', + '\161', '\165', '\040', '\061', '\012', '\160', '\162', '\113', + '\040', '\145', '\162', '\040', '\061', '\012', '\172', '\144', + '\103', '\040', '\144', '\145', '\040', '\061', '\012', '\171', + '\101', '\157', '\040', '\157', '\156', '\040', '\061', '\012', + '\144', '\114', '\164', '\040', '\163', '\164', '\040', '\061', + '\012', '\160', '\147', '\106', '\040', '\156', '\147', '\040', + '\061', '\012', '\166', '\147', '\127', '\040', '\156', '\147', + '\040', '\061', '\012', '\166', '\160', '\116', '\040', '\166', + '\141', '\040', '\061', '\012', '\111', '\166', '\170', '\040', + '\166', '\141', '\040', '\061', '\012', '\166', '\131', '\154', + '\040', '\154', '\145', '\040', '\061', '\012', '\170', '\122', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\152', + '\120', '\165', '\040', '\161', '\165', '\040', '\061', '\012', + '\117', '\161', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\152', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\144', '\160', '\110', '\040', '\144', '\145', + '\040', '\061', '\012', '\171', '\104', '\160', '\040', '\160', + '\162', '\040', '\061', '\012', '\170', '\146', '\112', '\040', + '\146', '\157', '\040', '\061', '\012', '\146', '\161', '\126', + '\040', '\161', '\165', '\040', '\061', '\012', '\145', '\102', + '\146', '\040', '\145', '\162', '\040', '\061', '\012', '\132', + '\153', '\167', '\040', '\153', '\141', '\040', '\061', '\012', + '\161', '\110', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\101', '\161', '\172', '\040', '\161', '\165', '\040', + '\061', '\012', '\142', '\116', '\167', '\040', '\167', '\141', + '\040', '\061', '\012', '\146', '\152', '\130', '\040', '\151', + '\152', '\040', '\061', '\012', '\146', '\161', '\123', '\040', + '\161', '\165', '\040', '\061', '\012', '\154', '\152', '\113', + '\040', '\154', '\145', '\040', '\061', '\012', '\107', '\153', + '\146', '\040', '\153', '\141', '\040', '\061', '\012', '\142', + '\123', '\146', '\040', '\142', '\145', '\040', '\061', '\012', + '\115', '\170', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\104', '\161', '\155', '\040', '\161', '\165', '\040', + '\061', '\012', '\150', '\113', '\160', '\040', '\164', '\150', + '\040', '\061', '\012', '\167', '\106', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\167', '\155', '\112', '\040', + '\155', '\145', '\040', '\061', '\012', '\166', '\172', '\124', + '\040', '\166', '\141', '\040', '\061', '\012', '\162', '\150', + '\112', '\040', '\164', '\150', '\040', '\061', '\012', '\156', + '\110', '\146', '\040', '\141', '\156', '\040', '\061', '\012', + '\152', '\112', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\161', '\127', '\171', '\040', '\161', '\165', '\040', + '\061', '\012', '\127', '\166', '\153', '\040', '\166', '\141', + '\040', '\061', '\012', '\147', '\153', '\102', '\040', '\156', + '\147', '\040', '\061', '\012', '\155', '\105', '\167', '\040', + '\155', '\145', '\040', '\061', '\012', '\125', '\147', '\170', + '\040', '\156', '\147', '\040', '\061', '\012', '\121', '\155', + '\171', '\040', '\155', '\145', '\040', '\061', '\012', '\114', + '\152', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\142', '\107', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\154', '\110', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\143', '\107', '\147', '\040', '\143', '\150', + '\040', '\061', '\012', '\147', '\106', '\153', '\040', '\156', + '\147', '\040', '\061', '\012', '\170', '\156', '\126', '\040', + '\141', '\156', '\040', '\061', '\012', '\145', '\106', '\171', + '\040', '\145', '\162', '\040', '\061', '\012', '\116', '\146', + '\155', '\040', '\155', '\145', '\040', '\061', '\012', '\150', + '\123', '\146', '\040', '\164', '\150', '\040', '\061', '\012', + '\147', '\130', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\110', '\146', '\040', '\146', '\157', '\040', + '\061', '\012', '\165', '\161', '\152', '\040', '\161', '\165', + '\040', '\061', '\012', '\167', '\130', '\141', '\040', '\141', + '\156', '\040', '\061', '\012', '\166', '\143', '\124', '\040', + '\143', '\150', '\040', '\061', '\012', '\165', '\112', '\167', + '\040', '\161', '\165', '\040', '\061', '\012', '\160', '\127', + '\170', '\040', '\160', '\162', '\040', '\061', '\012', '\161', + '\160', '\121', '\040', '\161', '\165', '\040', '\061', '\012', + '\150', '\161', '\105', '\040', '\164', '\150', '\040', '\061', + '\012', '\131', '\146', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\152', '\162', '\111', '\040', '\145', '\162', + '\040', '\061', '\012', '\143', '\147', '\113', '\040', '\143', + '\150', '\040', '\061', '\012', '\171', '\171', '\120', '\040', + '\156', '\171', '\040', '\061', '\012', '\132', '\155', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\114', '\153', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\145', + '\125', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\152', '\162', '\131', '\040', '\145', '\162', '\040', '\061', + '\012', '\153', '\106', '\163', '\040', '\163', '\172', '\040', + '\061', '\012', '\163', '\125', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\152', '\154', '\132', '\040', '\154', + '\145', '\040', '\061', '\012', '\143', '\156', '\126', '\040', + '\143', '\150', '\040', '\061', '\012', '\141', '\120', '\152', + '\040', '\141', '\156', '\040', '\061', '\012', '\155', '\152', + '\105', '\040', '\151', '\152', '\040', '\061', '\012', '\160', + '\132', '\154', '\040', '\154', '\145', '\040', '\061', '\012', + '\165', '\106', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\113', '\156', '\146', '\040', '\141', '\156', '\040', + '\061', '\012', '\106', '\160', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\150', '\146', '\122', '\040', '\164', + '\150', '\040', '\061', '\012', '\161', '\156', '\103', '\040', + '\141', '\156', '\040', '\061', '\012', '\104', '\154', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\146', '\162', + '\115', '\040', '\145', '\162', '\040', '\061', '\012', '\163', + '\146', '\102', '\040', '\163', '\172', '\040', '\061', '\012', + '\107', '\170', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\106', '\153', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\166', '\107', '\153', '\040', '\166', '\141', + '\040', '\061', '\012', '\147', '\122', '\155', '\040', '\156', + '\147', '\040', '\061', '\012', '\162', '\127', '\146', '\040', + '\145', '\162', '\040', '\061', '\012', '\162', '\131', '\166', + '\040', '\145', '\162', '\040', '\061', '\012', '\161', '\105', + '\144', '\040', '\161', '\165', '\040', '\061', '\012', '\161', + '\110', '\162', '\040', '\161', '\165', '\040', '\061', '\012', + '\123', '\155', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\154', '\106', '\160', '\040', '\154', '\145', '\040', + '\061', '\012', '\153', '\104', '\163', '\040', '\163', '\172', + '\040', '\061', '\012', '\144', '\123', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\162', '\114', '\167', '\040', + '\145', '\162', '\040', '\061', '\012', '\143', '\156', '\132', + '\040', '\141', '\156', '\040', '\061', '\012', '\127', '\152', + '\160', '\040', '\151', '\152', '\040', '\061', '\012', '\160', + '\124', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\113', '\143', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\113', '\163', '\040', '\166', '\141', '\040', + '\061', '\012', '\142', '\143', '\113', '\040', '\143', '\150', + '\040', '\061', '\012', '\166', '\167', '\171', '\040', '\166', + '\141', '\040', '\061', '\012', '\125', '\152', '\170', '\040', + '\151', '\152', '\040', '\061', '\012', '\121', '\166', '\162', + '\040', '\145', '\162', '\040', '\061', '\012', '\144', '\143', + '\126', '\040', '\143', '\150', '\040', '\061', '\012', '\170', + '\126', '\146', '\040', '\146', '\157', '\040', '\061', '\012', + '\165', '\111', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\154', '\116', '\040', '\154', '\145', '\040', + '\061', '\012', '\166', '\167', '\114', '\040', '\166', '\141', + '\040', '\061', '\012', '\146', '\127', '\160', '\040', '\160', + '\162', '\040', '\061', '\012', '\120', '\170', '\162', '\040', + '\145', '\162', '\040', '\061', '\012', '\162', '\122', '\142', + '\040', '\145', '\162', '\040', '\061', '\012', '\142', '\146', + '\104', '\040', '\142', '\145', '\040', '\061', '\012', '\171', + '\103', '\170', '\040', '\156', '\171', '\040', '\061', '\012', + '\156', '\112', '\163', '\040', '\141', '\156', '\040', '\061', + '\012', '\144', '\103', '\155', '\040', '\144', '\145', '\040', + '\061', '\012', '\143', '\142', '\107', '\040', '\143', '\150', + '\040', '\061', '\012', '\147', '\103', '\146', '\040', '\156', + '\147', '\040', '\061', '\012', '\164', '\155', '\126', '\040', + '\164', '\150', '\040', '\061', '\012', '\161', '\145', '\103', + '\040', '\161', '\165', '\040', '\061', '\012', '\153', '\156', + '\123', '\040', '\141', '\156', '\040', '\061', '\012', '\147', + '\167', '\131', '\040', '\156', '\147', '\040', '\061', '\012', + '\127', '\152', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\155', '\111', '\167', '\040', '\155', '\145', '\040', + '\061', '\012', '\161', '\152', '\127', '\040', '\161', '\165', + '\040', '\061', '\012', '\147', '\167', '\166', '\040', '\156', + '\147', '\040', '\061', '\012', '\161', '\112', '\167', '\040', + '\167', '\141', '\040', '\061', '\012', '\143', '\156', '\101', + '\040', '\141', '\156', '\040', '\061', '\012', '\142', '\102', + '\155', '\040', '\155', '\145', '\040', '\061', '\012', '\147', + '\106', '\167', '\040', '\156', '\147', '\040', '\061', '\012', + '\167', '\104', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\147', '\114', '\040', '\161', '\165', '\040', + '\061', '\012', '\154', '\125', '\141', '\040', '\141', '\156', + '\040', '\061', '\012', '\150', '\104', '\156', '\040', '\164', + '\150', '\040', '\061', '\012', '\153', '\110', '\170', '\040', + '\153', '\141', '\040', '\061', '\012', '\167', '\130', '\155', + '\040', '\155', '\145', '\040', '\061', '\012', '\161', '\171', + '\131', '\040', '\161', '\165', '\040', '\061', '\012', '\160', + '\153', '\104', '\040', '\153', '\141', '\040', '\061', '\012', + '\163', '\114', '\172', '\040', '\163', '\164', '\040', '\061', + '\012', '\172', '\170', '\106', '\040', '\163', '\172', '\040', + '\061', '\012', '\166', '\115', '\170', '\040', '\166', '\141', + '\040', '\061', '\012', '\160', '\154', '\122', '\040', '\154', + '\145', '\040', '\061', '\012', '\160', '\167', '\132', '\040', + '\160', '\162', '\040', '\061', '\012', '\160', '\131', '\144', + '\040', '\144', '\145', '\040', '\061', '\012', '\172', '\146', + '\114', '\040', '\163', '\172', '\040', '\061', '\012', '\172', + '\164', '\113', '\040', '\164', '\150', '\040', '\061', '\012', + '\155', '\124', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\144', '\103', '\160', '\040', '\144', '\145', '\040', + '\061', '\012', '\142', '\167', '\170', '\040', '\167', '\141', + '\040', '\061', '\012', '\170', '\103', '\163', '\040', '\163', + '\172', '\040', '\061', '\012', '\164', '\146', '\106', '\040', + '\164', '\150', '\040', '\061', '\012', '\114', '\156', '\161', + '\040', '\141', '\156', '\040', '\061', '\012', '\144', '\131', + '\151', '\040', '\151', '\156', '\040', '\061', '\012', '\160', + '\127', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\157', '\111', '\170', '\040', '\157', '\156', '\040', '\061', + '\012', '\171', '\167', '\105', '\040', '\167', '\141', '\040', + '\061', '\012', '\167', '\116', '\153', '\040', '\153', '\141', + '\040', '\061', '\012', '\152', '\167', '\117', '\040', '\151', + '\152', '\040', '\061', '\012', '\170', '\132', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\167', '\107', '\155', + '\040', '\155', '\145', '\040', '\061', '\012', '\143', '\126', + '\167', '\040', '\143', '\150', '\040', '\061', '\012', '\142', + '\152', '\113', '\040', '\151', '\152', '\040', '\061', '\012', + '\107', '\172', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\167', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\160', '\102', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\143', '\124', '\170', '\040', '\143', + '\150', '\040', '\061', '\012', '\162', '\110', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\127', '\163', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\170', '\105', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\171', + '\162', '\113', '\040', '\145', '\162', '\040', '\061', '\012', + '\155', '\115', '\142', '\040', '\155', '\145', '\040', '\061', + '\012', '\160', '\110', '\167', '\040', '\160', '\162', '\040', + '\061', '\012', '\143', '\152', '\116', '\040', '\143', '\150', + '\040', '\061', '\012', '\156', '\130', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\142', '\167', '\117', '\040', + '\167', '\141', '\040', '\061', '\012', '\146', '\154', '\102', + '\040', '\154', '\145', '\040', '\061', '\012', '\121', '\161', + '\152', '\040', '\161', '\165', '\040', '\061', '\012', '\155', + '\113', '\166', '\040', '\166', '\141', '\040', '\061', '\012', + '\146', '\106', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\146', '\107', '\040', '\167', '\141', '\040', + '\061', '\012', '\167', '\146', '\102', '\040', '\167', '\141', + '\040', '\061', '\012', '\112', '\161', '\153', '\040', '\161', + '\165', '\040', '\061', '\012', '\142', '\167', '\113', '\040', + '\167', '\141', '\040', '\061', '\012', '\150', '\150', '\111', + '\040', '\164', '\150', '\040', '\061', '\012', '\154', '\125', + '\145', '\040', '\145', '\162', '\040', '\061', '\012', '\167', + '\106', '\144', '\040', '\144', '\145', '\040', '\061', '\012', + '\166', '\153', '\124', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\114', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\146', '\150', '\102', '\040', '\164', '\150', + '\040', '\061', '\012', '\167', '\155', '\126', '\040', '\155', + '\145', '\040', '\061', '\012', '\164', '\155', '\106', '\040', + '\164', '\150', '\040', '\061', '\012', '\122', '\164', '\143', + '\040', '\164', '\150', '\040', '\061', '\012', '\144', '\171', + '\131', '\040', '\144', '\145', '\040', '\061', '\012', '\152', + '\171', '\167', '\040', '\151', '\152', '\040', '\061', '\012', + '\153', '\122', '\146', '\040', '\153', '\141', '\040', '\061', + '\012', '\146', '\130', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\132', '\156', '\172', '\040', '\141', '\156', + '\040', '\061', '\012', '\167', '\161', '\130', '\040', '\161', + '\165', '\040', '\061', '\012', '\165', '\115', '\170', '\040', + '\161', '\165', '\040', '\061', '\012', '\147', '\167', '\126', + '\040', '\156', '\147', '\040', '\061', '\012', '\120', '\142', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\144', + '\143', '\115', '\040', '\143', '\150', '\040', '\061', '\012', + '\156', '\120', '\172', '\040', '\141', '\156', '\040', '\061', + '\012', '\143', '\167', '\125', '\040', '\143', '\150', '\040', + '\061', '\012', '\166', '\112', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\147', '\171', '\121', '\040', '\156', + '\147', '\040', '\061', '\012', '\146', '\130', '\151', '\040', + '\151', '\156', '\040', '\061', '\012', '\142', '\163', '\132', + '\040', '\163', '\172', '\040', '\061', '\012', '\102', '\161', + '\151', '\040', '\161', '\165', '\040', '\061', '\012', '\166', + '\107', '\156', '\040', '\141', '\156', '\040', '\061', '\012', + '\153', '\156', '\116', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\131', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\164', '\124', '\142', '\040', '\164', '\150', + '\040', '\061', '\012', '\142', '\155', '\120', '\040', '\155', + '\145', '\040', '\061', '\012', '\152', '\160', '\132', '\040', + '\151', '\152', '\040', '\061', '\012', '\115', '\161', '\167', + '\040', '\161', '\165', '\040', '\061', '\012', '\166', '\152', + '\115', '\040', '\166', '\141', '\040', '\061', '\012', '\161', + '\126', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\152', '\165', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\102', '\153', '\040', '\145', '\162', '\040', + '\061', '\012', '\152', '\165', '\111', '\040', '\161', '\165', + '\040', '\061', '\012', '\172', '\105', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\172', '\127', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\146', '\172', '\110', + '\040', '\163', '\172', '\040', '\061', '\012', '\164', '\114', + '\170', '\040', '\164', '\150', '\040', '\061', '\012', '\116', + '\143', '\146', '\040', '\143', '\150', '\040', '\061', '\012', + '\153', '\146', '\116', '\040', '\153', '\141', '\040', '\061', + '\012', '\165', '\125', '\157', '\040', '\161', '\165', '\040', + '\061', '\012', '\146', '\103', '\163', '\040', '\163', '\172', + '\040', '\061', '\012', '\164', '\103', '\166', '\040', '\164', + '\150', '\040', '\061', '\012', '\163', '\125', '\171', '\040', + '\163', '\172', '\040', '\061', '\012', '\160', '\102', '\146', + '\040', '\160', '\162', '\040', '\061', '\012', '\152', '\102', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\166', + '\104', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\161', '\155', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\164', '\113', '\040', '\164', '\150', '\040', + '\061', '\012', '\161', '\143', '\123', '\040', '\143', '\150', + '\040', '\061', '\012', '\166', '\120', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\147', '\121', '\155', '\040', + '\156', '\147', '\040', '\061', '\012', '\150', '\172', '\122', + '\040', '\164', '\150', '\040', '\061', '\012', '\144', '\143', + '\114', '\040', '\143', '\150', '\040', '\061', '\012', '\170', + '\162', '\111', '\040', '\145', '\162', '\040', '\061', '\012', + '\144', '\166', '\116', '\040', '\166', '\141', '\040', '\061', + '\012', '\103', '\167', '\166', '\040', '\166', '\141', '\040', + '\061', '\012', '\170', '\150', '\121', '\040', '\164', '\150', + '\040', '\061', '\012', '\107', '\172', '\165', '\040', '\161', + '\165', '\040', '\061', '\012', '\160', '\144', '\117', '\040', + '\144', '\145', '\040', '\061', '\012', '\102', '\161', '\162', + '\040', '\161', '\165', '\040', '\061', '\012', '\166', '\114', + '\156', '\040', '\141', '\156', '\040', '\061', '\012', '\154', + '\170', '\146', '\040', '\154', '\145', '\040', '\061', '\012', + '\166', '\131', '\153', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\123', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\160', '\153', '\123', '\040', '\153', '\141', + '\040', '\061', '\012', '\172', '\113', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\164', '\120', '\155', '\040', + '\164', '\150', '\040', '\061', '\012', '\120', '\155', '\152', + '\040', '\151', '\152', '\040', '\061', '\012', '\154', '\127', + '\165', '\040', '\161', '\165', '\040', '\061', '\012', '\130', + '\165', '\165', '\040', '\161', '\165', '\040', '\061', '\012', + '\152', '\143', '\130', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\172', '\121', '\040', '\163', '\172', '\040', + '\061', '\012', '\107', '\172', '\167', '\040', '\163', '\172', + '\040', '\061', '\012', '\145', '\120', '\155', '\040', '\145', + '\162', '\040', '\061', '\012', '\146', '\167', '\127', '\040', + '\167', '\141', '\040', '\061', '\012', '\161', '\167', '\101', + '\040', '\161', '\165', '\040', '\061', '\012', '\166', '\121', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\142', + '\170', '\120', '\040', '\142', '\145', '\040', '\061', '\012', + '\144', '\155', '\104', '\040', '\144', '\145', '\040', '\061', + '\012', '\141', '\167', '\121', '\040', '\141', '\156', '\040', + '\061', '\012', '\146', '\126', '\146', '\040', '\146', '\157', + '\040', '\061', '\012', '\142', '\167', '\131', '\040', '\167', + '\141', '\040', '\061', '\012', '\132', '\170', '\164', '\040', + '\164', '\150', '\040', '\061', '\012', '\130', '\150', '\153', + '\040', '\164', '\150', '\040', '\061', '\012', '\147', '\131', + '\153', '\040', '\156', '\147', '\040', '\061', '\012', '\172', + '\103', '\146', '\040', '\163', '\172', '\040', '\061', '\012', + '\171', '\146', '\121', '\040', '\156', '\171', '\040', '\061', + '\012', '\172', '\107', '\167', '\040', '\163', '\172', '\040', + '\061', '\012', '\147', '\166', '\105', '\040', '\156', '\147', + '\040', '\061', '\012', '\147', '\103', '\166', '\040', '\156', + '\147', '\040', '\061', '\012', '\157', '\120', '\146', '\040', + '\157', '\156', '\040', '\061', '\012', '\172', '\130', '\151', + '\040', '\151', '\156', '\040', '\061', '\012', '\150', '\166', + '\111', '\040', '\164', '\150', '\040', '\061', '\012', '\150', + '\172', '\123', '\040', '\164', '\150', '\040', '\061', '\012', + '\155', '\146', '\130', '\040', '\155', '\145', '\040', '\061', + '\012', '\144', '\120', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\114', '\162', '\146', '\040', '\145', '\162', + '\040', '\061', '\012', '\154', '\162', '\107', '\040', '\145', + '\162', '\040', '\061', '\012', '\155', '\131', '\146', '\040', + '\155', '\145', '\040', '\061', '\012', '\150', '\116', '\152', + '\040', '\164', '\150', '\040', '\061', '\012', '\161', '\101', + '\152', '\040', '\161', '\165', '\040', '\061', '\012', '\163', + '\170', '\121', '\040', '\163', '\164', '\040', '\061', '\012', + '\153', '\124', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\117', '\146', '\040', '\161', '\165', '\040', + '\061', '\012', '\112', '\144', '\170', '\040', '\144', '\145', + '\040', '\061', '\012', '\163', '\167', '\113', '\040', '\163', + '\172', '\040', '\061', '\012', '\152', '\121', '\142', '\040', + '\151', '\152', '\040', '\061', '\012', '\104', '\161', '\160', + '\040', '\161', '\165', '\040', '\061', '\012', '\143', '\127', + '\166', '\040', '\143', '\150', '\040', '\061', '\012', '\144', + '\170', '\105', '\040', '\144', '\145', '\040', '\061', '\012', + '\163', '\130', '\152', '\040', '\163', '\172', '\040', '\061', + '\012', '\156', '\166', '\102', '\040', '\141', '\156', '\040', + '\061', '\012', '\167', '\130', '\146', '\040', '\167', '\141', + '\040', '\061', '\012', '\103', '\161', '\151', '\040', '\161', + '\165', '\040', '\061', '\012', '\142', '\172', '\127', '\040', + '\163', '\172', '\040', '\061', '\012', '\162', '\122', '\146', + '\040', '\145', '\162', '\040', '\061', '\012', '\155', '\132', + '\152', '\040', '\151', '\152', '\040', '\061', '\012', '\142', + '\156', '\106', '\040', '\141', '\156', '\040', '\061', '\012', + '\161', '\141', '\107', '\040', '\141', '\156', '\040', '\061', + '\012', '\102', '\161', '\163', '\040', '\161', '\165', '\040', + '\061', '\012', '\154', '\115', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\167', '\110', '\160', '\040', '\160', + '\162', '\040', '\061', '\012', '\114', '\152', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\115', '\167', '\146', + '\040', '\167', '\141', '\040', '\061', '\012', '\160', '\172', + '\113', '\040', '\163', '\172', '\040', '\061', '\012', '\155', + '\120', '\142', '\040', '\155', '\145', '\040', '\061', '\012', + '\161', '\152', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\122', '\162', '\040', '\145', '\162', '\040', + '\061', '\012', '\170', '\132', '\146', '\040', '\146', '\157', + '\040', '\061', '\012', '\156', '\161', '\107', '\040', '\141', + '\156', '\040', '\061', '\012', '\166', '\126', '\142', '\040', + '\166', '\141', '\040', '\061', '\012', '\160', '\152', '\103', + '\040', '\151', '\152', '\040', '\061', '\012', '\165', '\110', + '\154', '\040', '\161', '\165', '\040', '\061', '\012', '\152', + '\104', '\156', '\040', '\141', '\156', '\040', '\061', '\012', + '\160', '\161', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\161', '\153', '\040', '\161', '\165', '\040', + '\061', '\012', '\170', '\147', '\125', '\040', '\156', '\147', + '\040', '\061', '\012', '\167', '\112', '\170', '\040', '\167', + '\141', '\040', '\061', '\012', '\172', '\156', '\113', '\040', + '\141', '\156', '\040', '\061', '\012', '\162', '\150', '\102', + '\040', '\164', '\150', '\040', '\061', '\012', '\166', '\104', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\163', + '\112', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\130', '\153', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\156', '\112', '\040', '\141', '\156', '\040', + '\061', '\012', '\142', '\122', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\146', '\172', '\101', '\040', '\163', + '\172', '\040', '\061', '\012', '\142', '\121', '\145', '\040', + '\145', '\162', '\040', '\061', '\012', '\124', '\170', '\167', + '\040', '\167', '\141', '\040', '\061', '\012', '\142', '\153', + '\107', '\040', '\153', '\141', '\040', '\061', '\012', '\171', + '\167', '\132', '\040', '\167', '\141', '\040', '\061', '\012', + '\172', '\127', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\150', '\114', '\040', '\164', '\150', '\040', + '\061', '\012', '\147', '\155', '\106', '\040', '\156', '\147', + '\040', '\061', '\012', '\163', '\146', '\121', '\040', '\163', + '\172', '\040', '\061', '\012', '\172', '\155', '\107', '\040', + '\163', '\172', '\040', '\061', '\012', '\117', '\147', '\172', + '\040', '\156', '\147', '\040', '\061', '\012', '\170', '\165', + '\101', '\040', '\161', '\165', '\040', '\061', '\012', '\161', + '\101', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\172', '\104', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\126', '\165', '\040', '\161', '\165', '\040', + '\061', '\012', '\170', '\122', '\167', '\040', '\167', '\141', + '\040', '\061', '\012', '\170', '\155', '\115', '\040', '\155', + '\145', '\040', '\061', '\012', '\160', '\170', '\102', '\040', + '\160', '\162', '\040', '\061', '\012', '\172', '\164', '\124', + '\040', '\164', '\150', '\040', '\061', '\012', '\153', '\172', + '\112', '\040', '\163', '\172', '\040', '\061', '\012', '\156', + '\106', '\172', '\040', '\141', '\156', '\040', '\061', '\012', + '\165', '\126', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\156', '\121', '\040', '\141', '\156', '\040', + '\061', '\012', '\160', '\107', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\130', '\144', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\146', '\126', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\115', '\150', '\147', + '\040', '\164', '\150', '\040', '\061', '\012', '\130', '\161', + '\157', '\040', '\161', '\165', '\040', '\061', '\012', '\163', + '\110', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\152', '\167', '\103', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\153', '\107', '\040', '\166', '\141', '\040', + '\061', '\012', '\130', '\153', '\170', '\040', '\153', '\141', + '\040', '\061', '\012', '\164', '\122', '\147', '\040', '\164', + '\150', '\040', '\061', '\012', '\156', '\166', '\126', '\040', + '\141', '\156', '\040', '\061', '\012', '\161', '\167', '\107', + '\040', '\161', '\165', '\040', '\061', '\012', '\126', '\150', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\172', + '\167', '\117', '\040', '\163', '\172', '\040', '\061', '\012', + '\161', '\121', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\162', '\122', '\040', '\143', '\150', '\040', + '\061', '\012', '\115', '\162', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\157', '\121', '\145', '\040', '\145', + '\162', '\040', '\061', '\012', '\155', '\102', '\164', '\040', + '\164', '\150', '\040', '\061', '\012', '\166', '\125', '\171', + '\040', '\166', '\141', '\040', '\061', '\012', '\164', '\167', + '\127', '\040', '\164', '\150', '\040', '\061', '\012', '\121', + '\147', '\156', '\040', '\141', '\156', '\040', '\061', '\012', + '\116', '\170', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\150', '\106', '\040', '\164', '\150', '\040', + '\061', '\012', '\170', '\160', '\130', '\040', '\160', '\162', + '\040', '\061', '\012', '\146', '\166', '\104', '\040', '\166', + '\141', '\040', '\061', '\012', '\103', '\166', '\171', '\040', + '\166', '\141', '\040', '\061', '\012', '\157', '\110', '\152', + '\040', '\157', '\156', '\040', '\061', '\012', '\121', '\161', + '\157', '\040', '\161', '\165', '\040', '\061', '\012', '\166', + '\131', '\144', '\040', '\144', '\145', '\040', '\061', '\012', + '\170', '\150', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\132', '\146', '\040', '\146', '\157', '\040', + '\061', '\012', '\171', '\113', '\155', '\040', '\155', '\145', + '\040', '\061', '\012', '\170', '\131', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\146', '\143', '\125', '\040', + '\143', '\150', '\040', '\061', '\012', '\161', '\105', '\160', + '\040', '\161', '\165', '\040', '\061', '\012', '\152', '\130', + '\144', '\040', '\144', '\145', '\040', '\061', '\012', '\155', + '\154', '\121', '\040', '\154', '\145', '\040', '\061', '\012', + '\107', '\147', '\172', '\040', '\156', '\147', '\040', '\061', + '\012', '\143', '\114', '\160', '\040', '\143', '\150', '\040', + '\061', '\012', '\171', '\170', '\125', '\040', '\156', '\171', + '\040', '\061', '\012', '\147', '\166', '\112', '\040', '\156', + '\147', '\040', '\061', '\012', '\167', '\161', '\104', '\040', + '\161', '\165', '\040', '\061', '\012', '\166', '\163', '\116', + '\040', '\163', '\172', '\040', '\061', '\012', '\111', '\152', + '\146', '\040', '\151', '\152', '\040', '\061', '\012', '\152', + '\142', '\112', '\040', '\151', '\152', '\040', '\061', '\012', + '\142', '\115', '\170', '\040', '\142', '\145', '\040', '\061', + '\012', '\153', '\130', '\163', '\040', '\163', '\172', '\040', + '\061', '\012', '\147', '\162', '\124', '\040', '\156', '\147', + '\040', '\061', '\012', '\167', '\117', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\160', '\107', '\167', '\040', + '\160', '\162', '\040', '\061', '\012', '\107', '\153', '\144', + '\040', '\144', '\145', '\040', '\061', '\012', '\161', '\103', + '\152', '\040', '\161', '\165', '\040', '\061', '\012', '\150', + '\161', '\131', '\040', '\164', '\150', '\040', '\061', '\012', + '\162', '\104', '\160', '\040', '\145', '\162', '\040', '\061', + '\012', '\156', '\121', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\153', '\144', '\126', '\040', '\144', '\145', + '\040', '\061', '\012', '\142', '\147', '\123', '\040', '\156', + '\147', '\040', '\061', '\012', '\124', '\161', '\157', '\040', + '\161', '\165', '\040', '\061', '\012', '\146', '\105', '\152', + '\040', '\151', '\152', '\040', '\061', '\012', '\150', '\132', + '\163', '\040', '\164', '\150', '\040', '\061', '\012', '\152', + '\131', '\156', '\040', '\141', '\156', '\040', '\061', '\012', + '\142', '\120', '\170', '\040', '\142', '\145', '\040', '\061', + '\012', '\150', '\147', '\131', '\040', '\164', '\150', '\040', + '\061', '\012', '\120', '\166', '\171', '\040', '\166', '\141', + '\040', '\061', '\012', '\146', '\170', '\113', '\040', '\146', + '\157', '\040', '\061', '\012', '\110', '\167', '\167', '\040', + '\167', '\141', '\040', '\061', '\012', '\170', '\122', '\153', + '\040', '\153', '\141', '\040', '\061', '\012', '\144', '\155', + '\120', '\040', '\144', '\145', '\040', '\061', '\012', '\155', + '\143', '\131', '\040', '\143', '\150', '\040', '\061', '\012', + '\142', '\170', '\122', '\040', '\142', '\145', '\040', '\061', + '\012', '\114', '\163', '\154', '\040', '\154', '\145', '\040', + '\061', '\012', '\150', '\122', '\154', '\040', '\164', '\150', + '\040', '\061', '\012', '\151', '\167', '\121', '\040', '\151', + '\156', '\040', '\061', '\012', '\127', '\161', '\170', '\040', + '\161', '\165', '\040', '\061', '\012', '\153', '\146', '\126', + '\040', '\153', '\141', '\040', '\061', '\012', '\161', '\167', + '\116', '\040', '\161', '\165', '\040', '\061', '\012', '\121', + '\160', '\166', '\040', '\166', '\141', '\040', '\061', '\012', + '\155', '\162', '\117', '\040', '\145', '\162', '\040', '\061', + '\012', '\151', '\106', '\143', '\040', '\164', '\151', '\040', + '\061', '\012', '\167', '\172', '\104', '\040', '\163', '\172', + '\040', '\061', '\012', '\161', '\142', '\106', '\040', '\161', + '\165', '\040', '\061', '\012', '\170', '\146', '\123', '\040', + '\146', '\157', '\040', '\061', '\012', '\120', '\161', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\170', '\131', + '\142', '\040', '\142', '\145', '\040', '\061', '\012', '\154', + '\104', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\166', '\164', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\172', '\165', '\040', '\161', '\165', '\040', + '\061', '\012', '\170', '\152', '\113', '\040', '\151', '\152', + '\040', '\061', '\012', '\152', '\104', '\170', '\040', '\151', + '\152', '\040', '\061', '\012', '\156', '\103', '\152', '\040', + '\141', '\156', '\040', '\061', '\012', '\155', '\103', '\153', + '\040', '\153', '\141', '\040', '\061', '\012', '\161', '\170', + '\120', '\040', '\161', '\165', '\040', '\061', '\012', '\157', + '\115', '\166', '\040', '\157', '\156', '\040', '\061', '\012', + '\143', '\147', '\131', '\040', '\143', '\150', '\040', '\061', + '\012', '\127', '\161', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\153', '\153', '\121', '\040', '\153', '\141', + '\040', '\061', '\012', '\164', '\161', '\117', '\040', '\164', + '\150', '\040', '\061', '\012', '\152', '\156', '\103', '\040', + '\141', '\156', '\040', '\061', '\012', '\146', '\107', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\102', '\146', + '\166', '\040', '\166', '\141', '\040', '\061', '\012', '\166', + '\131', '\151', '\040', '\151', '\156', '\040', '\061', '\012', + '\160', '\143', '\114', '\040', '\143', '\150', '\040', '\061', + '\012', '\106', '\147', '\160', '\040', '\156', '\147', '\040', + '\061', '\012', '\152', '\164', '\122', '\040', '\164', '\150', + '\040', '\061', '\012', '\166', '\150', '\106', '\040', '\164', + '\150', '\040', '\061', '\012', '\167', '\125', '\151', '\040', + '\151', '\156', '\040', '\061', '\012', '\156', '\116', '\152', + '\040', '\141', '\156', '\040', '\061', '\012', '\152', '\124', + '\167', '\040', '\151', '\152', '\040', '\061', '\012', '\161', + '\163', '\115', '\040', '\161', '\165', '\040', '\061', '\012', + '\141', '\112', '\147', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\121', '\145', '\040', '\145', '\162', '\040', + '\061', '\012', '\107', '\156', '\152', '\040', '\141', '\156', + '\040', '\061', '\012', '\146', '\155', '\115', '\040', '\155', + '\145', '\040', '\061', '\012', '\172', '\161', '\115', '\040', + '\161', '\165', '\040', '\061', '\012', '\147', '\152', '\132', + '\040', '\156', '\147', '\040', '\061', '\012', '\156', '\170', + '\110', '\040', '\141', '\156', '\040', '\061', '\012', '\143', + '\144', '\117', '\040', '\143', '\150', '\040', '\061', '\012', + '\141', '\101', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\125', '\166', '\040', '\164', '\150', '\040', + '\061', '\012', '\150', '\130', '\153', '\040', '\164', '\150', + '\040', '\061', '\012', '\161', '\102', '\170', '\040', '\161', + '\165', '\040', '\061', '\012', '\164', '\147', '\113', '\040', + '\164', '\150', '\040', '\061', '\012', '\146', '\132', '\171', + '\040', '\156', '\171', '\040', '\061', '\012', '\112', '\153', + '\170', '\040', '\153', '\141', '\040', '\061', '\012', '\160', + '\166', '\104', '\040', '\166', '\141', '\040', '\061', '\012', + '\142', '\155', '\124', '\040', '\155', '\145', '\040', '\061', + '\012', '\157', '\131', '\170', '\040', '\157', '\156', '\040', + '\061', '\012', '\150', '\167', '\126', '\040', '\164', '\150', + '\040', '\061', '\012', '\155', '\152', '\102', '\040', '\151', + '\152', '\040', '\061', '\012', '\142', '\131', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\151', '\110', '\170', + '\040', '\151', '\156', '\040', '\061', '\012', '\154', '\131', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\161', + '\103', '\151', '\040', '\151', '\156', '\040', '\061', '\012', + '\146', '\150', '\122', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\104', '\146', '\040', '\141', '\156', '\040', + '\061', '\012', '\150', '\103', '\144', '\040', '\164', '\150', + '\040', '\061', '\012', '\154', '\170', '\102', '\040', '\154', + '\145', '\040', '\061', '\012', '\145', '\130', '\152', '\040', + '\145', '\162', '\040', '\061', '\012', '\146', '\166', '\127', + '\040', '\166', '\141', '\040', '\061', '\012', '\143', '\143', + '\127', '\040', '\143', '\150', '\040', '\061', '\012', '\144', + '\124', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\163', '\161', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\116', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\172', '\153', '\115', '\040', '\163', '\172', + '\040', '\061', '\012', '\154', '\122', '\166', '\040', '\154', + '\145', '\040', '\061', '\012', '\161', '\156', '\111', '\040', + '\141', '\156', '\040', '\061', '\012', '\170', '\167', '\103', + '\040', '\167', '\141', '\040', '\061', '\012', '\172', '\161', + '\131', '\040', '\161', '\165', '\040', '\061', '\012', '\171', + '\121', '\142', '\040', '\142', '\145', '\040', '\061', '\012', + '\170', '\162', '\103', '\040', '\145', '\162', '\040', '\061', + '\012', '\170', '\106', '\155', '\040', '\155', '\145', '\040', + '\061', '\012', '\157', '\145', '\121', '\040', '\145', '\162', + '\040', '\061', '\012', '\155', '\114', '\154', '\040', '\154', + '\145', '\040', '\061', '\012', '\152', '\167', '\124', '\040', + '\151', '\152', '\040', '\061', '\012', '\146', '\167', '\104', + '\040', '\167', '\141', '\040', '\061', '\012', '\166', '\160', + '\105', '\040', '\166', '\141', '\040', '\061', '\012', '\146', + '\154', '\131', '\040', '\154', '\145', '\040', '\061', '\012', + '\163', '\122', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\123', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\167', '\165', '\122', '\040', '\161', '\165', + '\040', '\061', '\012', '\167', '\162', '\111', '\040', '\145', + '\162', '\040', '\061', '\012', '\131', '\163', '\156', '\040', + '\163', '\164', '\040', '\061', '\012', '\126', '\150', '\152', + '\040', '\164', '\150', '\040', '\061', '\012', '\103', '\161', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\131', + '\147', '\142', '\040', '\156', '\147', '\040', '\061', '\012', + '\150', '\120', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\153', '\102', '\040', '\153', '\141', '\040', + '\061', '\012', '\164', '\122', '\161', '\040', '\164', '\150', + '\040', '\061', '\012', '\141', '\152', '\121', '\040', '\141', + '\156', '\040', '\061', '\012', '\150', '\143', '\122', '\040', + '\164', '\150', '\040', '\061', '\012', '\166', '\104', '\167', + '\040', '\166', '\141', '\040', '\061', '\012', '\160', '\121', + '\156', '\040', '\141', '\156', '\040', '\061', '\012', '\170', + '\145', '\125', '\040', '\145', '\162', '\040', '\061', '\012', + '\166', '\143', '\115', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\126', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\142', '\122', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\165', '\106', '\170', '\040', '\161', + '\165', '\040', '\061', '\012', '\146', '\142', '\127', '\040', + '\142', '\145', '\040', '\061', '\012', '\165', '\125', '\166', + '\040', '\161', '\165', '\040', '\061', '\012', '\116', '\150', + '\166', '\040', '\164', '\150', '\040', '\061', '\012', '\131', + '\153', '\170', '\040', '\153', '\141', '\040', '\061', '\012', + '\127', '\164', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\115', '\172', '\152', '\040', '\163', '\172', '\040', + '\061', '\012', '\156', '\160', '\124', '\040', '\151', '\156', + '\040', '\061', '\012', '\130', '\161', '\153', '\040', '\161', + '\165', '\040', '\061', '\012', '\170', '\167', '\116', '\040', + '\167', '\141', '\040', '\061', '\012', '\150', '\130', '\167', + '\040', '\164', '\150', '\040', '\061', '\012', '\172', '\114', + '\142', '\040', '\163', '\172', '\040', '\061', '\012', '\107', + '\170', '\171', '\040', '\156', '\171', '\040', '\061', '\012', + '\144', '\104', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\102', '\146', '\171', '\040', '\156', '\171', '\040', + '\061', '\012', '\146', '\153', '\170', '\040', '\153', '\141', + '\040', '\061', '\012', '\152', '\117', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\104', '\144', '\153', '\040', + '\144', '\145', '\040', '\061', '\012', '\116', '\152', '\160', + '\040', '\151', '\152', '\040', '\061', '\012', '\170', '\152', + '\112', '\040', '\151', '\152', '\040', '\061', '\012', '\161', + '\150', '\123', '\040', '\164', '\150', '\040', '\061', '\012', + '\121', '\167', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\171', '\127', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\156', '\106', '\166', '\040', '\141', '\156', + '\040', '\061', '\012', '\160', '\114', '\142', '\040', '\160', + '\162', '\040', '\061', '\012', '\161', '\142', '\102', '\040', + '\161', '\165', '\040', '\061', '\012', '\163', '\155', '\130', + '\040', '\163', '\172', '\040', '\061', '\012', '\164', '\156', + '\132', '\040', '\164', '\150', '\040', '\061', '\012', '\172', + '\121', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\106', '\172', '\142', '\040', '\163', '\172', '\040', '\061', + '\012', '\143', '\116', '\142', '\040', '\143', '\150', '\040', + '\061', '\012', '\150', '\160', '\126', '\040', '\164', '\150', + '\040', '\061', '\012', '\102', '\170', '\172', '\040', '\163', + '\172', '\040', '\061', '\012', '\170', '\147', '\107', '\040', + '\156', '\147', '\040', '\061', '\012', '\122', '\154', '\152', + '\040', '\154', '\145', '\040', '\061', '\012', '\151', '\110', + '\161', '\040', '\151', '\156', '\040', '\061', '\012', '\163', + '\167', '\116', '\040', '\163', '\172', '\040', '\061', '\012', + '\116', '\152', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\120', '\153', '\040', '\153', '\141', '\040', + '\061', '\012', '\157', '\122', '\166', '\040', '\157', '\156', + '\040', '\061', '\012', '\160', '\112', '\163', '\040', '\163', + '\172', '\040', '\061', '\012', '\153', '\132', '\167', '\040', + '\153', '\141', '\040', '\061', '\012', '\166', '\126', '\163', + '\040', '\163', '\164', '\040', '\061', '\012', '\126', '\142', + '\167', '\040', '\167', '\141', '\040', '\061', '\012', '\106', + '\146', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\155', '\172', '\121', '\040', '\163', '\172', '\040', '\061', + '\012', '\107', '\166', '\154', '\040', '\154', '\145', '\040', + '\061', '\012', '\120', '\147', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\154', '\120', '\160', '\040', '\154', + '\145', '\040', '\061', '\012', '\166', '\103', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\153', '\116', '\146', + '\040', '\153', '\141', '\040', '\061', '\012', '\142', '\155', + '\104', '\040', '\155', '\145', '\040', '\061', '\012', '\155', + '\127', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\163', '\154', '\106', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\151', '\130', '\040', '\151', '\156', '\040', + '\061', '\012', '\171', '\122', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\154', '\161', '\170', '\040', '\161', + '\165', '\040', '\061', '\012', '\161', '\154', '\152', '\040', + '\161', '\165', '\040', '\061', '\012', '\163', '\146', '\132', + '\040', '\163', '\172', '\040', '\061', '\012', '\127', '\146', + '\171', '\040', '\156', '\171', '\040', '\061', '\012', '\166', + '\162', '\117', '\040', '\145', '\162', '\040', '\061', '\012', + '\147', '\170', '\124', '\040', '\156', '\147', '\040', '\061', + '\012', '\154', '\167', '\105', '\040', '\154', '\145', '\040', + '\061', '\012', '\161', '\144', '\112', '\040', '\161', '\165', + '\040', '\061', '\012', '\131', '\160', '\153', '\040', '\153', + '\141', '\040', '\061', '\012', '\121', '\160', '\146', '\040', + '\160', '\162', '\040', '\061', '\012', '\132', '\156', '\167', + '\040', '\141', '\156', '\040', '\061', '\012', '\142', '\146', + '\112', '\040', '\142', '\145', '\040', '\061', '\012', '\161', + '\121', '\171', '\040', '\161', '\165', '\040', '\061', '\012', + '\161', '\101', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\141', '\161', '\127', '\040', '\141', '\156', '\040', + '\061', '\012', '\161', '\161', '\111', '\040', '\161', '\165', + '\040', '\061', '\012', '\114', '\167', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\116', '\156', '\167', '\040', + '\141', '\156', '\040', '\061', '\012', '\143', '\114', '\166', + '\040', '\143', '\150', '\040', '\061', '\012', '\127', '\164', + '\170', '\040', '\164', '\150', '\040', '\061', '\012', '\161', + '\143', '\161', '\040', '\143', '\150', '\040', '\061', '\012', + '\163', '\152', '\122', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\127', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\132', '\155', '\170', '\040', '\155', '\145', + '\040', '\061', '\012', '\161', '\132', '\147', '\040', '\161', + '\165', '\040', '\061', '\012', '\164', '\131', '\172', '\040', + '\164', '\150', '\040', '\061', '\012', '\147', '\126', '\170', + '\040', '\156', '\147', '\040', '\061', '\012', '\155', '\130', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\156', + '\167', '\112', '\040', '\141', '\156', '\040', '\061', '\012', + '\152', '\167', '\132', '\040', '\151', '\152', '\040', '\061', + '\012', '\154', '\167', '\114', '\040', '\154', '\145', '\040', + '\061', '\012', '\145', '\107', '\170', '\040', '\145', '\162', + '\040', '\061', '\012', '\123', '\161', '\153', '\040', '\161', + '\165', '\040', '\061', '\012', '\147', '\102', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\172', '\163', '\123', + '\040', '\163', '\172', '\040', '\061', '\012', '\153', '\156', + '\121', '\040', '\141', '\156', '\040', '\061', '\012', '\116', + '\156', '\146', '\040', '\141', '\156', '\040', '\061', '\012', + '\161', '\155', '\124', '\040', '\161', '\165', '\040', '\061', + '\012', '\123', '\161', '\160', '\040', '\161', '\165', '\040', + '\061', '\012', '\146', '\146', '\121', '\040', '\146', '\157', + '\040', '\061', '\012', '\126', '\143', '\166', '\040', '\143', + '\150', '\040', '\061', '\012', '\146', '\155', '\104', '\040', + '\155', '\145', '\040', '\061', '\012', '\172', '\131', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\142', '\101', + '\170', '\040', '\142', '\145', '\040', '\061', '\012', '\156', + '\142', '\127', '\040', '\141', '\156', '\040', '\061', '\012', + '\147', '\112', '\155', '\040', '\156', '\147', '\040', '\061', + '\012', '\112', '\167', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\155', '\170', '\112', '\040', '\155', '\145', + '\040', '\061', '\012', '\170', '\142', '\103', '\040', '\142', + '\145', '\040', '\061', '\012', '\122', '\142', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\170', '\132', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\142', '\112', + '\171', '\040', '\142', '\145', '\040', '\061', '\012', '\130', + '\171', '\153', '\040', '\153', '\141', '\040', '\061', '\012', + '\172', '\153', '\126', '\040', '\163', '\172', '\040', '\061', + '\012', '\165', '\157', '\106', '\040', '\161', '\165', '\040', + '\061', '\012', '\142', '\143', '\125', '\040', '\143', '\150', + '\040', '\061', '\012', '\143', '\132', '\161', '\040', '\143', + '\150', '\040', '\061', '\012', '\162', '\120', '\155', '\040', + '\145', '\162', '\040', '\061', '\012', '\162', '\107', '\156', + '\040', '\141', '\156', '\040', '\061', '\012', '\154', '\143', + '\114', '\040', '\143', '\150', '\040', '\061', '\012', '\162', + '\126', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\103', '\147', '\167', '\040', '\156', '\147', '\040', '\061', + '\012', '\103', '\164', '\161', '\040', '\164', '\150', '\040', + '\061', '\012', '\145', '\107', '\166', '\040', '\145', '\162', + '\040', '\061', '\012', '\122', '\172', '\163', '\040', '\163', + '\164', '\040', '\061', '\012', '\121', '\150', '\172', '\040', + '\164', '\150', '\040', '\061', '\012', '\163', '\114', '\166', + '\040', '\166', '\141', '\040', '\061', '\012', '\126', '\161', + '\155', '\040', '\161', '\165', '\040', '\061', '\012', '\171', + '\144', '\112', '\040', '\144', '\145', '\040', '\061', '\012', + '\170', '\126', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\164', '\114', '\153', '\040', '\164', '\150', '\040', + '\061', '\012', '\161', '\146', '\171', '\040', '\161', '\165', + '\040', '\061', '\012', '\167', '\170', '\126', '\040', '\167', + '\141', '\040', '\061', '\012', '\171', '\122', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\126', '\170', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\161', '\131', + '\172', '\040', '\161', '\165', '\040', '\061', '\012', '\172', + '\150', '\115', '\040', '\164', '\150', '\040', '\061', '\012', + '\155', '\114', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\132', '\166', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\106', '\166', '\155', '\040', '\166', '\141', + '\040', '\061', '\012', '\150', '\143', '\115', '\040', '\164', + '\150', '\040', '\061', '\012', '\115', '\167', '\160', '\040', + '\167', '\141', '\040', '\061', '\012', '\143', '\124', '\147', + '\040', '\143', '\150', '\040', '\061', '\012', '\154', '\130', + '\162', '\040', '\145', '\162', '\040', '\061', '\012', '\146', + '\121', '\145', '\040', '\145', '\162', '\040', '\061', '\012', + '\112', '\142', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\171', '\146', '\107', '\040', '\156', '\171', '\040', + '\061', '\012', '\160', '\150', '\113', '\040', '\164', '\150', + '\040', '\061', '\012', '\147', '\152', '\110', '\040', '\156', + '\147', '\040', '\061', '\012', '\127', '\144', '\147', '\040', + '\144', '\145', '\040', '\061', '\012', '\160', '\120', '\156', + '\040', '\141', '\156', '\040', '\061', '\012', '\102', '\167', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\172', + '\156', '\102', '\040', '\141', '\156', '\040', '\061', '\012', + '\146', '\167', '\112', '\040', '\167', '\141', '\040', '\061', + '\012', '\165', '\164', '\121', '\040', '\164', '\150', '\040', + '\061', '\012', '\143', '\152', '\103', '\040', '\143', '\150', + '\040', '\061', '\012', '\146', '\126', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\143', '\124', '\155', '\040', + '\143', '\150', '\040', '\061', '\012', '\167', '\115', '\166', + '\040', '\166', '\141', '\040', '\061', '\012', '\113', '\147', + '\153', '\040', '\156', '\147', '\040', '\061', '\012', '\156', + '\122', '\144', '\040', '\141', '\156', '\040', '\061', '\012', + '\155', '\115', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\152', '\121', '\040', '\151', '\152', '\040', + '\061', '\012', '\161', '\131', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\163', '\131', '\152', '\040', '\163', + '\164', '\040', '\061', '\012', '\152', '\116', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\161', '\130', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\167', '\172', + '\102', '\040', '\163', '\172', '\040', '\061', '\012', '\123', + '\152', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\161', '\164', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\131', '\151', '\040', '\151', '\156', '\040', + '\061', '\012', '\147', '\154', '\124', '\040', '\156', '\147', + '\040', '\061', '\012', '\125', '\165', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\165', '\117', '\160', '\040', + '\161', '\165', '\040', '\061', '\012', '\151', '\102', '\170', + '\040', '\151', '\156', '\040', '\061', '\012', '\122', '\161', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\172', + '\127', '\152', '\040', '\163', '\172', '\040', '\061', '\012', + '\110', '\143', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\116', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\172', '\121', '\162', '\040', '\145', '\162', + '\040', '\061', '\012', '\151', '\110', '\144', '\040', '\151', + '\156', '\040', '\061', '\012', '\127', '\160', '\170', '\040', + '\160', '\162', '\040', '\061', '\012', '\156', '\146', '\131', + '\040', '\141', '\156', '\040', '\061', '\012', '\122', '\153', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\113', + '\161', '\147', '\040', '\161', '\165', '\040', '\061', '\012', + '\107', '\146', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\153', '\162', '\103', '\040', '\145', '\162', '\040', + '\061', '\012', '\127', '\150', '\143', '\040', '\164', '\150', + '\040', '\061', '\012', '\154', '\152', '\115', '\040', '\154', + '\145', '\040', '\061', '\012', '\171', '\170', '\107', '\040', + '\156', '\171', '\040', '\061', '\012', '\146', '\160', '\127', + '\040', '\160', '\162', '\040', '\061', '\012', '\142', '\143', + '\106', '\040', '\143', '\150', '\040', '\061', '\012', '\153', + '\162', '\170', '\040', '\145', '\162', '\040', '\061', '\012', + '\165', '\104', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\106', '\172', '\157', '\040', '\157', '\156', '\040', + '\061', '\012', '\167', '\120', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\114', '\146', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\102', '\153', '\160', '\040', + '\153', '\141', '\040', '\061', '\012', '\130', '\153', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\152', '\170', + '\110', '\040', '\151', '\152', '\040', '\061', '\012', '\166', + '\111', '\152', '\040', '\166', '\141', '\040', '\061', '\012', + '\147', '\124', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\105', '\152', '\040', '\164', '\150', '\040', + '\061', '\012', '\146', '\161', '\102', '\040', '\161', '\165', + '\040', '\061', '\012', '\152', '\154', '\104', '\040', '\154', + '\145', '\040', '\061', '\012', '\164', '\106', '\146', '\040', + '\164', '\150', '\040', '\061', '\012', '\116', '\146', '\167', + '\040', '\167', '\141', '\040', '\061', '\012', '\106', '\161', + '\145', '\040', '\161', '\165', '\040', '\061', '\012', '\124', + '\172', '\160', '\040', '\163', '\172', '\040', '\061', '\012', + '\163', '\112', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\111', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\144', '\106', '\142', '\040', '\144', '\145', + '\040', '\061', '\012', '\161', '\172', '\105', '\040', '\161', + '\165', '\040', '\061', '\012', '\155', '\126', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\126', '\161', '\141', + '\040', '\141', '\156', '\040', '\061', '\012', '\142', '\161', + '\115', '\040', '\161', '\165', '\040', '\061', '\012', '\155', + '\144', '\112', '\040', '\144', '\145', '\040', '\061', '\012', + '\144', '\111', '\160', '\040', '\144', '\145', '\040', '\061', + '\012', '\132', '\156', '\170', '\040', '\141', '\156', '\040', + '\061', '\012', '\152', '\153', '\113', '\040', '\151', '\152', + '\040', '\061', '\012', '\162', '\146', '\121', '\040', '\145', + '\162', '\040', '\061', '\012', '\170', '\153', '\111', '\040', + '\153', '\165', '\040', '\061', '\012', '\146', '\111', '\157', + '\040', '\162', '\157', '\040', '\061', '\012', '\154', '\161', + '\126', '\040', '\161', '\165', '\040', '\061', '\012', '\121', + '\160', '\144', '\040', '\144', '\145', '\040', '\061', '\012', + '\160', '\101', '\170', '\040', '\160', '\162', '\040', '\061', + '\012', '\162', '\162', '\121', '\040', '\145', '\162', '\040', + '\061', '\012', '\142', '\111', '\165', '\040', '\161', '\165', + '\040', '\061', '\012', '\170', '\104', '\167', '\040', '\167', + '\141', '\040', '\061', '\012', '\157', '\110', '\170', '\040', + '\157', '\156', '\040', '\061', '\012', '\167', '\112', '\167', + '\040', '\167', '\141', '\040', '\061', '\012', '\103', '\161', + '\166', '\040', '\161', '\165', '\040', '\061', '\012', '\171', + '\166', '\102', '\040', '\166', '\141', '\040', '\061', '\012', + '\171', '\161', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\114', '\170', '\040', '\145', '\162', '\040', + '\061', '\012', '\106', '\172', '\170', '\040', '\163', '\172', + '\040', '\061', '\012', '\144', '\132', '\146', '\040', '\144', + '\145', '\040', '\061', '\012', '\116', '\161', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\122', '\156', '\172', + '\040', '\141', '\156', '\040', '\061', '\012', '\150', '\124', + '\143', '\040', '\164', '\150', '\040', '\061', '\012', '\142', + '\126', '\142', '\040', '\142', '\145', '\040', '\061', '\012', + '\106', '\144', '\155', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\146', '\166', '\040', '\166', '\141', '\040', + '\061', '\012', '\150', '\167', '\123', '\040', '\164', '\150', + '\040', '\061', '\012', '\172', '\120', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\107', '\170', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\106', '\166', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\155', '\132', + '\162', '\040', '\145', '\162', '\040', '\061', '\012', '\172', + '\126', '\162', '\040', '\145', '\162', '\040', '\061', '\012', + '\155', '\102', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\130', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\120', '\154', '\167', '\040', '\154', '\145', + '\040', '\061', '\012', '\116', '\154', '\170', '\040', '\154', + '\145', '\040', '\061', '\012', '\152', '\103', '\144', '\040', + '\144', '\145', '\040', '\061', '\012', '\113', '\167', '\166', + '\040', '\166', '\141', '\040', '\061', '\012', '\112', '\161', + '\141', '\040', '\141', '\156', '\040', '\061', '\012', '\172', + '\107', '\163', '\040', '\163', '\164', '\040', '\061', '\012', + '\146', '\165', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\172', '\114', '\040', '\163', '\172', '\040', + '\061', '\012', '\151', '\106', '\170', '\040', '\151', '\156', + '\040', '\061', '\012', '\146', '\124', '\155', '\040', '\155', + '\145', '\040', '\061', '\012', '\171', '\127', '\144', '\040', + '\144', '\145', '\040', '\061', '\012', '\143', '\110', '\166', + '\040', '\143', '\150', '\040', '\061', '\012', '\146', '\106', + '\153', '\040', '\153', '\141', '\040', '\061', '\012', '\155', + '\161', '\144', '\040', '\161', '\165', '\040', '\061', '\012', + '\141', '\121', '\153', '\040', '\141', '\156', '\040', '\061', + '\012', '\165', '\104', '\146', '\040', '\161', '\165', '\040', + '\061', '\012', '\126', '\142', '\146', '\040', '\142', '\145', + '\040', '\061', '\012', '\160', '\147', '\112', '\040', '\156', + '\147', '\040', '\061', '\012', '\146', '\153', '\116', '\040', + '\153', '\141', '\040', '\061', '\012', '\160', '\102', '\155', + '\040', '\155', '\145', '\040', '\061', '\012', '\102', '\144', + '\166', '\040', '\144', '\145', '\040', '\061', '\012', '\152', + '\155', '\127', '\040', '\151', '\152', '\040', '\061', '\012', + '\112', '\166', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\130', '\160', '\153', '\040', '\153', '\141', '\040', + '\061', '\012', '\161', '\121', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\153', '\144', '\107', '\040', '\144', + '\145', '\040', '\061', '\012', '\161', '\153', '\120', '\040', + '\161', '\165', '\040', '\061', '\012', '\143', '\123', '\144', + '\040', '\143', '\150', '\040', '\061', '\012', '\106', '\144', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\161', + '\147', '\113', '\040', '\161', '\165', '\040', '\061', '\012', + '\161', '\144', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\116', '\166', '\040', '\161', '\165', '\040', + '\061', '\012', '\145', '\126', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\144', '\146', '\101', '\040', '\144', + '\145', '\040', '\061', '\012', '\110', '\172', '\171', '\040', + '\163', '\172', '\040', '\061', '\012', '\154', '\127', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\166', '\170', + '\110', '\040', '\166', '\141', '\040', '\061', '\012', '\150', + '\170', '\127', '\040', '\164', '\150', '\040', '\061', '\012', + '\113', '\150', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\121', '\142', '\040', '\142', '\145', '\040', + '\061', '\012', '\160', '\167', '\124', '\040', '\160', '\162', + '\040', '\061', '\012', '\114', '\167', '\146', '\040', '\167', + '\141', '\040', '\061', '\012', '\172', '\104', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\153', '\170', '\113', + '\040', '\153', '\141', '\040', '\061', '\012', '\155', '\164', + '\131', '\040', '\164', '\150', '\040', '\061', '\012', '\142', + '\150', '\124', '\040', '\164', '\150', '\040', '\061', '\012', + '\171', '\167', '\122', '\040', '\167', '\141', '\040', '\061', + '\012', '\152', '\111', '\141', '\040', '\141', '\156', '\040', + '\061', '\012', '\127', '\172', '\145', '\040', '\145', '\162', + '\040', '\061', '\012', '\150', '\161', '\113', '\040', '\164', + '\150', '\040', '\061', '\012', '\146', '\154', '\132', '\040', + '\154', '\145', '\040', '\061', '\012', '\161', '\115', '\151', + '\040', '\151', '\156', '\040', '\061', '\012', '\167', '\160', + '\122', '\040', '\167', '\141', '\040', '\061', '\012', '\161', + '\110', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\141', '\117', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\144', '\153', '\125', '\040', '\144', '\145', '\040', + '\061', '\012', '\166', '\122', '\162', '\040', '\145', '\162', + '\040', '\061', '\012', '\166', '\152', '\130', '\040', '\166', + '\141', '\040', '\061', '\012', '\143', '\165', '\121', '\040', + '\143', '\150', '\040', '\061', '\012', '\161', '\155', '\112', + '\040', '\161', '\165', '\040', '\061', '\012', '\165', '\165', + '\112', '\040', '\157', '\165', '\040', '\061', '\012', '\171', + '\127', '\170', '\040', '\156', '\171', '\040', '\061', '\012', + '\150', '\125', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\172', '\120', '\040', '\166', '\141', '\040', + '\061', '\012', '\162', '\123', '\170', '\040', '\145', '\162', + '\040', '\061', '\012', '\161', '\147', '\171', '\040', '\161', + '\165', '\040', '\061', '\012', '\122', '\172', '\146', '\040', + '\163', '\172', '\040', '\061', '\012', '\172', '\152', '\102', + '\040', '\163', '\172', '\040', '\061', '\012', '\123', '\152', + '\170', '\040', '\151', '\152', '\040', '\061', '\012', '\170', + '\146', '\101', '\040', '\146', '\157', '\040', '\061', '\012', + '\146', '\110', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\153', '\102', '\040', '\161', '\165', '\040', + '\061', '\012', '\143', '\144', '\106', '\040', '\143', '\150', + '\040', '\061', '\012', '\146', '\127', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\152', '\142', '\101', '\040', + '\151', '\152', '\040', '\061', '\012', '\102', '\155', '\142', + '\040', '\155', '\145', '\040', '\061', '\012', '\171', '\152', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\162', + '\170', '\132', '\040', '\145', '\162', '\040', '\061', '\012', + '\126', '\155', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\151', '\111', '\161', '\040', '\151', '\156', '\040', + '\061', '\012', '\127', '\147', '\154', '\040', '\156', '\147', + '\040', '\061', '\012', '\155', '\122', '\160', '\040', '\155', + '\145', '\040', '\061', '\012', '\167', '\166', '\123', '\040', + '\166', '\141', '\040', '\061', '\012', '\125', '\166', '\171', + '\040', '\166', '\141', '\040', '\061', '\012', '\171', '\160', + '\121', '\040', '\160', '\162', '\040', '\061', '\012', '\166', + '\106', '\167', '\040', '\166', '\157', '\040', '\061', '\012', + '\146', '\161', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\167', '\112', '\040', '\163', '\164', '\040', + '\061', '\012', '\112', '\162', '\170', '\040', '\145', '\162', + '\040', '\061', '\012', '\143', '\170', '\105', '\040', '\143', + '\150', '\040', '\061', '\012', '\154', '\132', '\153', '\040', + '\154', '\145', '\040', '\061', '\012', '\146', '\126', '\156', + '\040', '\141', '\156', '\040', '\061', '\012', '\142', '\150', + '\132', '\040', '\164', '\150', '\040', '\061', '\012', '\152', + '\150', '\122', '\040', '\164', '\150', '\040', '\061', '\012', + '\166', '\123', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\121', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\146', '\110', '\166', '\040', '\166', '\141', + '\040', '\061', '\012', '\166', '\165', '\116', '\040', '\161', + '\165', '\040', '\061', '\012', '\152', '\160', '\107', '\040', + '\151', '\152', '\040', '\061', '\012', '\120', '\153', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\147', '\121', + '\142', '\040', '\156', '\147', '\040', '\061', '\012', '\160', + '\106', '\163', '\040', '\163', '\164', '\040', '\061', '\012', + '\107', '\152', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\163', '\113', '\040', '\164', '\150', '\040', + '\061', '\012', '\164', '\167', '\170', '\040', '\164', '\150', + '\040', '\061', '\012', '\171', '\171', '\121', '\040', '\156', + '\171', '\040', '\061', '\012', '\144', '\161', '\106', '\040', + '\161', '\165', '\040', '\061', '\012', '\142', '\110', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\161', '\115', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\161', + '\113', '\166', '\040', '\161', '\165', '\040', '\061', '\012', + '\172', '\114', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\155', '\117', '\040', '\151', '\152', '\040', + '\061', '\012', '\167', '\102', '\153', '\040', '\153', '\141', + '\040', '\061', '\012', '\160', '\152', '\121', '\040', '\151', + '\152', '\040', '\061', '\012', '\170', '\132', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\161', '\111', '\165', + '\040', '\165', '\156', '\040', '\061', '\012', '\171', '\143', + '\131', '\040', '\143', '\150', '\040', '\061', '\012', '\155', + '\104', '\146', '\040', '\155', '\145', '\040', '\061', '\012', + '\171', '\112', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\111', '\163', '\170', '\040', '\163', '\164', '\040', + '\061', '\012', '\121', '\161', '\162', '\040', '\161', '\165', + '\040', '\061', '\012', '\106', '\153', '\167', '\040', '\153', + '\141', '\040', '\061', '\012', '\103', '\160', '\152', '\040', + '\151', '\152', '\040', '\061', '\012', '\131', '\166', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\172', '\152', + '\107', '\040', '\163', '\172', '\040', '\061', '\012', '\147', + '\107', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\130', '\144', '\155', '\040', '\144', '\145', '\040', '\061', + '\012', '\150', '\102', '\166', '\040', '\164', '\150', '\040', + '\061', '\012', '\127', '\170', '\152', '\040', '\151', '\152', + '\040', '\061', '\012', '\131', '\167', '\142', '\040', '\157', + '\167', '\040', '\061', '\012', '\126', '\164', '\161', '\040', + '\164', '\150', '\040', '\061', '\012', '\164', '\152', '\131', + '\040', '\164', '\150', '\040', '\061', '\012', '\152', '\104', + '\152', '\040', '\151', '\152', '\040', '\061', '\012', '\165', + '\107', '\144', '\040', '\161', '\165', '\040', '\061', '\012', + '\167', '\166', '\106', '\040', '\166', '\141', '\040', '\061', + '\012', '\165', '\161', '\147', '\040', '\161', '\165', '\040', + '\061', '\012', '\122', '\167', '\160', '\040', '\160', '\162', + '\040', '\061', '\012', '\102', '\147', '\142', '\040', '\156', + '\147', '\040', '\061', '\012', '\155', '\156', '\125', '\040', + '\141', '\156', '\040', '\061', '\012', '\144', '\160', '\111', + '\040', '\144', '\145', '\040', '\061', '\012', '\167', '\113', + '\144', '\040', '\144', '\145', '\040', '\061', '\012', '\171', + '\130', '\172', '\040', '\163', '\172', '\040', '\061', '\012', + '\153', '\114', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\147', '\131', '\170', '\040', '\156', '\147', '\040', + '\061', '\012', '\161', '\170', '\153', '\040', '\161', '\165', + '\040', '\061', '\012', '\110', '\150', '\171', '\040', '\164', + '\150', '\040', '\061', '\012', '\146', '\160', '\112', '\040', + '\160', '\162', '\040', '\061', '\012', '\143', '\126', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\153', '\126', + '\166', '\040', '\166', '\141', '\040', '\061', '\012', '\112', + '\172', '\163', '\040', '\163', '\164', '\040', '\061', '\012', + '\156', '\104', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\152', '\106', '\040', '\164', '\150', '\040', + '\061', '\012', '\142', '\132', '\152', '\040', '\151', '\152', + '\040', '\061', '\012', '\155', '\161', '\114', '\040', '\161', + '\165', '\040', '\061', '\012', '\150', '\106', '\164', '\040', + '\164', '\150', '\040', '\061', '\012', '\156', '\116', '\167', + '\040', '\141', '\156', '\040', '\061', '\012', '\167', '\106', + '\166', '\040', '\166', '\141', '\040', '\061', '\012', '\147', + '\110', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\161', '\122', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\112', '\170', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\126', '\160', '\166', '\040', '\166', '\141', + '\040', '\061', '\012', '\156', '\115', '\153', '\040', '\141', + '\156', '\040', '\061', '\012', '\164', '\152', '\116', '\040', + '\164', '\150', '\040', '\061', '\012', '\146', '\150', '\121', + '\040', '\164', '\150', '\040', '\061', '\012', '\142', '\160', + '\104', '\040', '\160', '\162', '\040', '\061', '\012', '\104', + '\146', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\152', '\171', '\117', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\150', '\126', '\040', '\164', '\150', '\040', + '\061', '\012', '\153', '\126', '\153', '\040', '\153', '\141', + '\040', '\061', '\012', '\156', '\113', '\143', '\040', '\141', + '\156', '\040', '\061', '\012', '\152', '\153', '\112', '\040', + '\151', '\152', '\040', '\061', '\012', '\143', '\167', '\123', + '\040', '\143', '\150', '\040', '\061', '\012', '\157', '\104', + '\146', '\040', '\157', '\156', '\040', '\061', '\012', '\155', + '\153', '\131', '\040', '\153', '\141', '\040', '\061', '\012', + '\147', '\144', '\126', '\040', '\156', '\147', '\040', '\061', + '\012', '\130', '\150', '\142', '\040', '\164', '\150', '\040', + '\061', '\012', '\152', '\125', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\141', '\112', '\146', '\040', '\141', + '\156', '\040', '\061', '\012', '\121', '\170', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\170', '\172', '\123', + '\040', '\163', '\172', '\040', '\061', '\012', '\166', '\125', + '\167', '\040', '\166', '\141', '\040', '\061', '\012', '\150', + '\124', '\152', '\040', '\164', '\150', '\040', '\061', '\012', + '\157', '\126', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\144', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\146', '\110', '\163', '\040', '\163', '\164', + '\040', '\061', '\012', '\170', '\113', '\153', '\040', '\153', + '\141', '\040', '\061', '\012', '\142', '\106', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\147', '\127', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\131', '\161', + '\141', '\040', '\141', '\156', '\040', '\061', '\012', '\144', + '\155', '\110', '\040', '\144', '\145', '\040', '\061', '\012', + '\124', '\164', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\121', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\152', '\106', '\150', '\040', '\151', '\152', + '\040', '\061', '\012', '\146', '\143', '\131', '\040', '\143', + '\150', '\040', '\061', '\012', '\146', '\163', '\122', '\040', + '\163', '\164', '\040', '\061', '\012', '\151', '\127', '\147', + '\040', '\151', '\156', '\040', '\061', '\012', '\130', '\171', + '\152', '\040', '\151', '\152', '\040', '\061', '\012', '\130', + '\152', '\163', '\040', '\163', '\164', '\040', '\061', '\012', + '\170', '\160', '\142', '\040', '\160', '\162', '\040', '\061', + '\012', '\154', '\172', '\131', '\040', '\154', '\145', '\040', + '\061', '\012', '\160', '\172', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\144', '\126', '\167', '\040', '\144', + '\145', '\040', '\061', '\012', '\111', '\152', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\146', '\166', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\126', '\156', + '\142', '\040', '\141', '\156', '\040', '\061', '\012', '\172', + '\144', '\110', '\040', '\144', '\145', '\040', '\061', '\012', + '\143', '\104', '\144', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\161', '\111', '\040', '\161', '\165', '\040', + '\061', '\012', '\171', '\146', '\125', '\040', '\156', '\171', + '\040', '\061', '\012', '\161', '\157', '\110', '\040', '\161', + '\165', '\040', '\061', '\012', '\170', '\153', '\167', '\040', + '\153', '\141', '\040', '\061', '\012', '\113', '\143', '\153', + '\040', '\143', '\150', '\040', '\061', '\012', '\155', '\125', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\172', + '\127', '\155', '\040', '\163', '\172', '\040', '\061', '\012', + '\102', '\146', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\162', '\121', '\152', '\040', '\145', '\162', '\040', + '\061', '\012', '\161', '\145', '\127', '\040', '\161', '\165', + '\040', '\061', '\012', '\161', '\160', '\103', '\040', '\161', + '\165', '\040', '\061', '\012', '\157', '\161', '\115', '\040', + '\161', '\165', '\040', '\061', '\012', '\160', '\172', '\117', + '\040', '\163', '\172', '\040', '\061', '\012', '\143', '\152', + '\121', '\040', '\143', '\150', '\040', '\061', '\012', '\172', + '\124', '\170', '\040', '\163', '\172', '\040', '\061', '\012', + '\147', '\122', '\167', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\144', '\121', '\040', '\144', '\145', '\040', + '\061', '\012', '\167', '\142', '\121', '\040', '\167', '\141', + '\040', '\061', '\012', '\121', '\160', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\172', '\111', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\171', '\170', '\116', + '\040', '\156', '\171', '\040', '\061', '\012', '\156', '\103', + '\153', '\040', '\141', '\156', '\040', '\061', '\012', '\112', + '\161', '\172', '\040', '\161', '\165', '\040', '\061', '\012', + '\144', '\105', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\144', '\105', '\040', '\156', '\147', '\040', + '\061', '\012', '\167', '\103', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\160', '\121', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\166', '\113', '\145', '\040', + '\145', '\162', '\040', '\061', '\012', '\124', '\152', '\155', + '\040', '\151', '\152', '\040', '\061', '\012', '\132', '\143', + '\171', '\040', '\143', '\150', '\040', '\061', '\012', '\153', + '\155', '\122', '\040', '\153', '\141', '\040', '\061', '\012', + '\143', '\124', '\160', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\161', '\105', '\040', '\161', '\165', '\040', + '\061', '\012', '\166', '\166', '\132', '\040', '\166', '\141', + '\040', '\061', '\012', '\143', '\114', '\167', '\040', '\143', + '\150', '\040', '\061', '\012', '\157', '\111', '\167', '\040', + '\157', '\156', '\040', '\061', '\012', '\170', '\152', '\107', + '\040', '\151', '\152', '\040', '\061', '\012', '\166', '\164', + '\125', '\040', '\164', '\150', '\040', '\061', '\012', '\150', + '\143', '\110', '\040', '\164', '\150', '\040', '\061', '\012', + '\170', '\147', '\124', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\161', '\122', '\040', '\161', '\165', '\040', + '\061', '\012', '\167', '\165', '\115', '\040', '\161', '\165', + '\040', '\061', '\012', '\170', '\163', '\131', '\040', '\163', + '\164', '\040', '\061', '\012', '\152', '\103', '\165', '\040', + '\161', '\165', '\040', '\061', '\012', '\106', '\142', '\156', + '\040', '\141', '\156', '\040', '\061', '\012', '\143', '\161', + '\110', '\040', '\143', '\150', '\040', '\061', '\012', '\130', + '\152', '\172', '\040', '\151', '\152', '\040', '\061', '\012', + '\146', '\147', '\122', '\040', '\156', '\147', '\040', '\061', + '\012', '\171', '\151', '\130', '\040', '\151', '\156', '\040', + '\061', '\012', '\161', '\156', '\117', '\040', '\141', '\156', + '\040', '\061', '\012', '\167', '\155', '\116', '\040', '\155', + '\145', '\040', '\061', '\012', '\167', '\147', '\110', '\040', + '\156', '\147', '\040', '\061', '\012', '\164', '\142', '\132', + '\040', '\164', '\150', '\040', '\061', '\012', '\130', '\153', + '\163', '\040', '\163', '\164', '\040', '\061', '\012', '\160', + '\172', '\103', '\040', '\160', '\157', '\040', '\061', '\012', + '\154', '\146', '\130', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\102', '\165', '\040', '\165', '\156', '\040', + '\061', '\012', '\155', '\114', '\167', '\040', '\155', '\145', + '\040', '\061', '\012', '\160', '\155', '\131', '\040', '\155', + '\145', '\040', '\061', '\012', '\170', '\161', '\105', '\040', + '\161', '\165', '\040', '\061', '\012', '\162', '\152', '\131', + '\040', '\145', '\162', '\040', '\061', '\012', '\166', '\162', + '\110', '\040', '\145', '\162', '\040', '\061', '\012', '\111', + '\165', '\146', '\040', '\161', '\165', '\040', '\061', '\012', + '\171', '\146', '\104', '\040', '\156', '\171', '\040', '\061', + '\012', '\143', '\154', '\107', '\040', '\143', '\150', '\040', + '\061', '\012', '\143', '\144', '\132', '\040', '\143', '\150', + '\040', '\061', '\012', '\145', '\124', '\144', '\040', '\145', + '\162', '\040', '\061', '\012', '\154', '\130', '\166', '\040', + '\154', '\145', '\040', '\061', '\012', '\153', '\160', '\126', + '\040', '\153', '\141', '\040', '\061', '\012', '\163', '\132', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\127', + '\170', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\166', '\155', '\112', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\153', '\105', '\040', '\164', '\150', '\040', + '\061', '\012', '\160', '\125', '\167', '\040', '\160', '\162', + '\040', '\061', '\012', '\103', '\161', '\144', '\040', '\161', + '\165', '\040', '\061', '\012', '\167', '\103', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\160', '\170', '\121', + '\040', '\160', '\162', '\040', '\061', '\012', '\131', '\167', + '\160', '\040', '\160', '\162', '\040', '\061', '\012', '\170', + '\167', '\142', '\040', '\167', '\141', '\040', '\061', '\012', + '\127', '\152', '\155', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\161', '\121', '\040', '\161', '\165', '\040', + '\061', '\012', '\147', '\124', '\160', '\040', '\156', '\147', + '\040', '\061', '\012', '\165', '\132', '\166', '\040', '\161', + '\165', '\040', '\061', '\012', '\155', '\144', '\110', '\040', + '\144', '\145', '\040', '\061', '\012', '\152', '\165', '\121', + '\040', '\161', '\165', '\040', '\061', '\012', '\147', '\126', + '\155', '\040', '\156', '\147', '\040', '\061', '\012', '\172', + '\152', '\131', '\040', '\151', '\152', '\040', '\061', '\012', + '\146', '\150', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\146', '\104', '\040', '\167', '\141', '\040', + '\061', '\012', '\132', '\152', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\151', '\120', '\166', '\040', '\151', + '\156', '\040', '\061', '\012', '\155', '\172', '\127', '\040', + '\163', '\172', '\040', '\061', '\012', '\166', '\130', '\155', + '\040', '\166', '\141', '\040', '\061', '\012', '\146', '\105', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\117', + '\172', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\147', '\105', '\160', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\104', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\132', '\154', '\167', '\040', '\154', '\145', + '\040', '\061', '\012', '\172', '\142', '\122', '\040', '\163', + '\172', '\040', '\061', '\012', '\172', '\103', '\164', '\040', + '\164', '\150', '\040', '\061', '\012', '\167', '\157', '\131', + '\040', '\157', '\156', '\040', '\061', '\012', '\160', '\153', + '\124', '\040', '\153', '\141', '\040', '\061', '\012', '\153', + '\142', '\111', '\040', '\153', '\141', '\040', '\061', '\012', + '\150', '\144', '\127', '\040', '\144', '\145', '\040', '\061', + '\012', '\110', '\163', '\170', '\040', '\163', '\164', '\040', + '\061', '\012', '\172', '\160', '\130', '\040', '\163', '\172', + '\040', '\061', '\012', '\172', '\146', '\126', '\040', '\163', + '\172', '\040', '\061', '\012', '\104', '\150', '\153', '\040', + '\164', '\150', '\040', '\061', '\012', '\167', '\115', '\160', + '\040', '\160', '\162', '\040', '\061', '\012', '\150', '\172', + '\112', '\040', '\164', '\150', '\040', '\061', '\012', '\114', + '\167', '\160', '\040', '\160', '\162', '\040', '\061', '\012', + '\172', '\155', '\116', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\146', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\163', '\152', '\121', '\040', '\163', '\172', + '\040', '\061', '\012', '\172', '\153', '\113', '\040', '\163', + '\172', '\040', '\061', '\012', '\142', '\102', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\142', '\144', '\105', + '\040', '\144', '\145', '\040', '\061', '\012', '\121', '\170', + '\156', '\040', '\141', '\156', '\040', '\061', '\012', '\152', + '\161', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\152', '\150', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\131', '\166', '\040', '\166', '\141', '\040', + '\061', '\012', '\170', '\150', '\105', '\040', '\164', '\150', + '\040', '\061', '\012', '\143', '\142', '\106', '\040', '\143', + '\150', '\040', '\061', '\012', '\112', '\156', '\142', '\040', + '\141', '\156', '\040', '\061', '\012', '\152', '\170', '\116', + '\040', '\151', '\152', '\040', '\061', '\012', '\146', '\131', + '\170', '\040', '\146', '\157', '\040', '\061', '\012', '\150', + '\112', '\160', '\040', '\164', '\150', '\040', '\061', '\012', + '\143', '\122', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\156', '\123', '\040', '\141', '\156', '\040', + '\061', '\012', '\166', '\114', '\160', '\040', '\166', '\141', + '\040', '\061', '\012', '\143', '\102', '\144', '\040', '\143', + '\150', '\040', '\061', '\012', '\161', '\161', '\125', '\040', + '\161', '\165', '\040', '\061', '\012', '\123', '\144', '\144', + '\040', '\144', '\145', '\040', '\061', '\012', '\170', '\145', + '\132', '\040', '\145', '\162', '\040', '\061', '\012', '\112', + '\167', '\157', '\040', '\157', '\156', '\040', '\061', '\012', + '\144', '\120', '\146', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\116', '\154', '\040', '\154', '\145', '\040', + '\061', '\012', '\153', '\111', '\142', '\040', '\153', '\141', + '\040', '\061', '\012', '\143', '\142', '\114', '\040', '\143', + '\150', '\040', '\061', '\012', '\121', '\144', '\162', '\040', + '\145', '\162', '\040', '\061', '\012', '\115', '\146', '\142', + '\040', '\142', '\145', '\040', '\061', '\012', '\152', '\112', + '\154', '\040', '\154', '\145', '\040', '\061', '\012', '\155', + '\170', '\131', '\040', '\155', '\145', '\040', '\061', '\012', + '\154', '\106', '\144', '\040', '\154', '\145', '\040', '\061', + '\012', '\164', '\167', '\124', '\040', '\164', '\150', '\040', + '\061', '\012', '\153', '\106', '\153', '\040', '\153', '\141', + '\040', '\061', '\012', '\143', '\162', '\102', '\040', '\143', + '\150', '\040', '\061', '\012', '\152', '\122', '\162', '\040', + '\145', '\162', '\040', '\061', '\012', '\110', '\164', '\172', + '\040', '\164', '\150', '\040', '\061', '\012', '\160', '\131', + '\146', '\040', '\160', '\162', '\040', '\061', '\012', '\162', + '\126', '\143', '\040', '\145', '\162', '\040', '\061', '\012', + '\166', '\122', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\126', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\172', '\160', '\101', '\040', '\163', '\172', + '\040', '\061', '\012', '\147', '\154', '\131', '\040', '\154', + '\145', '\040', '\061', '\012', '\163', '\116', '\152', '\040', + '\151', '\152', '\040', '\061', '\012', '\166', '\113', '\170', + '\040', '\166', '\141', '\040', '\061', '\012', '\164', '\166', + '\102', '\040', '\164', '\150', '\040', '\061', '\012', '\131', + '\152', '\146', '\040', '\151', '\152', '\040', '\061', '\012', + '\155', '\167', '\120', '\040', '\155', '\145', '\040', '\061', + '\012', '\112', '\171', '\142', '\040', '\142', '\145', '\040', + '\061', '\012', '\164', '\102', '\143', '\040', '\164', '\150', + '\040', '\061', '\012', '\147', '\123', '\142', '\040', '\156', + '\147', '\040', '\061', '\012', '\143', '\115', '\154', '\040', + '\143', '\150', '\040', '\061', '\012', '\147', '\152', '\112', + '\040', '\156', '\147', '\040', '\061', '\012', '\144', '\131', + '\172', '\040', '\144', '\145', '\040', '\061', '\012', '\172', + '\120', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\153', '\161', '\102', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\106', '\166', '\040', '\163', '\164', '\040', + '\061', '\012', '\170', '\153', '\110', '\040', '\153', '\141', + '\040', '\061', '\012', '\146', '\132', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\171', '\150', '\122', '\040', + '\164', '\150', '\040', '\061', '\012', '\142', '\167', '\116', + '\040', '\167', '\141', '\040', '\061', '\012', '\161', '\152', + '\107', '\040', '\161', '\165', '\040', '\061', '\012', '\156', + '\121', '\155', '\040', '\141', '\156', '\040', '\061', '\012', + '\161', '\115', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\143', '\127', '\040', '\143', '\150', '\040', + '\061', '\012', '\161', '\112', '\166', '\040', '\161', '\165', + '\040', '\061', '\012', '\147', '\124', '\155', '\040', '\156', + '\147', '\040', '\061', '\012', '\153', '\155', '\121', '\040', + '\153', '\141', '\040', '\061', '\012', '\127', '\154', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\153', '\131', + '\146', '\040', '\153', '\141', '\040', '\061', '\012', '\145', + '\112', '\160', '\040', '\145', '\162', '\040', '\061', '\012', + '\124', '\153', '\142', '\040', '\153', '\141', '\040', '\061', + '\012', '\150', '\146', '\115', '\040', '\164', '\150', '\040', + '\061', '\012', '\156', '\170', '\131', '\040', '\141', '\156', + '\040', '\061', '\012', '\160', '\104', '\154', '\040', '\154', + '\145', '\040', '\061', '\012', '\167', '\143', '\116', '\040', + '\143', '\150', '\040', '\061', '\012', '\160', '\121', '\141', + '\040', '\141', '\156', '\040', '\061', '\012', '\157', '\150', + '\132', '\040', '\164', '\150', '\040', '\061', '\012', '\170', + '\122', '\172', '\040', '\163', '\172', '\040', '\061', '\012', + '\154', '\142', '\126', '\040', '\154', '\145', '\040', '\061', + '\012', '\154', '\113', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\167', '\170', '\102', '\040', '\167', '\141', + '\040', '\061', '\012', '\114', '\167', '\167', '\040', '\167', + '\141', '\040', '\061', '\012', '\146', '\161', '\121', '\040', + '\161', '\165', '\040', '\061', '\012', '\153', '\153', '\132', + '\040', '\153', '\141', '\040', '\061', '\012', '\151', '\167', + '\117', '\040', '\151', '\156', '\040', '\061', '\012', '\144', + '\147', '\125', '\040', '\156', '\147', '\040', '\061', '\012', + '\144', '\166', '\117', '\040', '\144', '\145', '\040', '\061', + '\012', '\160', '\104', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\153', '\166', '\113', '\040', '\153', '\141', + '\040', '\061', '\012', '\152', '\154', '\126', '\040', '\154', + '\145', '\040', '\061', '\012', '\170', '\130', '\144', '\040', + '\144', '\145', '\040', '\061', '\012', '\171', '\153', '\106', + '\040', '\153', '\165', '\040', '\061', '\012', '\151', '\171', + '\124', '\040', '\151', '\156', '\040', '\061', '\012', '\125', + '\146', '\170', '\040', '\146', '\157', '\040', '\061', '\012', + '\156', '\172', '\125', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\142', '\110', '\040', '\142', '\165', '\040', + '\061', '\012', '\154', '\123', '\142', '\040', '\154', '\145', + '\040', '\061', '\012', '\130', '\160', '\146', '\040', '\160', + '\162', '\040', '\061', '\012', '\125', '\166', '\146', '\040', + '\166', '\141', '\040', '\061', '\012', '\171', '\171', '\106', + '\040', '\156', '\171', '\040', '\061', '\012', '\146', '\170', + '\120', '\040', '\146', '\157', '\040', '\061', '\012', '\152', + '\131', '\165', '\040', '\161', '\165', '\040', '\061', '\012', + '\161', '\152', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\170', '\114', '\040', '\156', '\147', '\040', + '\061', '\012', '\160', '\167', '\111', '\040', '\160', '\162', + '\040', '\061', '\012', '\152', '\125', '\145', '\040', '\145', + '\162', '\040', '\061', '\012', '\162', '\106', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\146', '\163', '\106', + '\040', '\163', '\164', '\040', '\061', '\012', '\143', '\144', + '\127', '\040', '\143', '\150', '\040', '\061', '\012', '\130', + '\167', '\160', '\040', '\160', '\162', '\040', '\061', '\012', + '\170', '\144', '\110', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\131', '\163', '\040', '\151', '\152', '\040', + '\061', '\012', '\142', '\106', '\144', '\040', '\144', '\145', + '\040', '\061', '\012', '\161', '\111', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\171', '\111', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\166', '\124', '\144', + '\040', '\144', '\145', '\040', '\061', '\012', '\167', '\146', + '\105', '\040', '\167', '\141', '\040', '\061', '\012', '\161', + '\122', '\142', '\040', '\161', '\165', '\040', '\061', '\012', + '\171', '\150', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\115', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\143', '\160', '\102', '\040', '\143', '\150', + '\040', '\061', '\012', '\164', '\170', '\116', '\040', '\164', + '\150', '\040', '\061', '\012', '\153', '\120', '\144', '\040', + '\144', '\145', '\040', '\061', '\012', '\156', '\142', '\102', + '\040', '\141', '\156', '\040', '\061', '\012', '\163', '\153', + '\121', '\040', '\163', '\164', '\040', '\061', '\012', '\165', + '\113', '\167', '\040', '\161', '\165', '\040', '\061', '\012', + '\167', '\121', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\153', '\127', '\146', '\040', '\153', '\141', '\040', + '\061', '\012', '\167', '\161', '\101', '\040', '\161', '\165', + '\040', '\061', '\012', '\143', '\167', '\101', '\040', '\143', + '\150', '\040', '\061', '\012', '\166', '\112', '\153', '\040', + '\153', '\141', '\040', '\061', '\012', '\150', '\143', '\104', + '\040', '\164', '\150', '\040', '\061', '\012', '\156', '\146', + '\113', '\040', '\141', '\156', '\040', '\061', '\012', '\165', + '\130', '\146', '\040', '\161', '\165', '\040', '\061', '\012', + '\143', '\147', '\101', '\040', '\143', '\150', '\040', '\061', + '\012', '\120', '\152', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\114', '\161', '\163', '\040', '\161', '\165', + '\040', '\061', '\012', '\172', '\167', '\103', '\040', '\163', + '\172', '\040', '\061', '\012', '\154', '\152', '\116', '\040', + '\154', '\145', '\040', '\061', '\012', '\166', '\153', '\120', + '\040', '\153', '\141', '\040', '\061', '\012', '\122', '\161', + '\160', '\040', '\161', '\165', '\040', '\061', '\012', '\172', + '\107', '\170', '\040', '\163', '\172', '\040', '\061', '\012', + '\152', '\120', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\142', '\124', '\040', '\153', '\141', '\040', + '\061', '\012', '\153', '\160', '\121', '\040', '\153', '\141', + '\040', '\061', '\012', '\115', '\172', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\107', '\152', '\163', '\040', + '\163', '\164', '\040', '\061', '\012', '\153', '\104', '\154', + '\040', '\154', '\145', '\040', '\061', '\012', '\152', '\167', + '\122', '\040', '\151', '\152', '\040', '\061', '\012', '\127', + '\171', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\161', '\170', '\123', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\107', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\127', '\166', '\162', '\040', '\145', '\162', + '\040', '\061', '\012', '\172', '\116', '\170', '\040', '\163', + '\172', '\040', '\061', '\012', '\166', '\103', '\155', '\040', + '\166', '\141', '\040', '\061', '\012', '\150', '\154', '\104', + '\040', '\164', '\150', '\040', '\061', '\012', '\166', '\102', + '\160', '\040', '\166', '\141', '\040', '\061', '\012', '\155', + '\112', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\150', '\106', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\104', '\155', '\040', '\166', '\141', '\040', + '\061', '\012', '\160', '\146', '\103', '\040', '\160', '\162', + '\040', '\061', '\012', '\114', '\160', '\171', '\040', '\160', + '\162', '\040', '\061', '\012', '\106', '\150', '\144', '\040', + '\164', '\150', '\040', '\061', '\012', '\144', '\170', '\123', + '\040', '\144', '\145', '\040', '\061', '\012', '\167', '\127', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\106', + '\147', '\156', '\040', '\141', '\156', '\040', '\061', '\012', + '\156', '\106', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\143', '\170', '\106', '\040', '\143', '\150', '\040', + '\061', '\012', '\141', '\126', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\123', '\161', '\170', '\040', '\161', + '\165', '\040', '\061', '\012', '\126', '\152', '\172', '\040', + '\151', '\152', '\040', '\061', '\012', '\172', '\156', '\103', + '\040', '\141', '\156', '\040', '\061', '\012', '\161', '\161', + '\166', '\040', '\161', '\165', '\040', '\061', '\012', '\172', + '\162', '\132', '\040', '\145', '\162', '\040', '\061', '\012', + '\142', '\116', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\156', '\166', '\127', '\040', '\141', '\156', '\040', + '\061', '\012', '\121', '\171', '\142', '\040', '\142', '\145', + '\040', '\061', '\012', '\106', '\150', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\152', '\107', '\166', '\040', + '\151', '\152', '\040', '\061', '\012', '\147', '\114', '\160', + '\040', '\156', '\147', '\040', '\061', '\012', '\147', '\114', + '\142', '\040', '\156', '\147', '\040', '\061', '\012', '\161', + '\113', '\152', '\040', '\161', '\165', '\040', '\061', '\012', + '\150', '\112', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\152', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\156', '\121', '\161', '\040', '\141', '\156', + '\040', '\061', '\012', '\156', '\160', '\130', '\040', '\141', + '\156', '\040', '\061', '\012', '\161', '\151', '\117', '\040', + '\151', '\156', '\040', '\061', '\012', '\166', '\166', '\107', + '\040', '\166', '\141', '\040', '\061', '\012', '\152', '\117', + '\170', '\040', '\151', '\152', '\040', '\061', '\012', '\150', + '\150', '\105', '\040', '\164', '\150', '\040', '\061', '\012', + '\166', '\144', '\116', '\040', '\144', '\145', '\040', '\061', + '\012', '\103', '\172', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\147', '\152', '\125', '\040', '\156', '\147', + '\040', '\061', '\012', '\150', '\126', '\142', '\040', '\164', + '\150', '\040', '\061', '\012', '\113', '\143', '\147', '\040', + '\143', '\150', '\040', '\061', '\012', '\144', '\166', '\110', + '\040', '\144', '\145', '\040', '\061', '\012', '\167', '\164', + '\104', '\040', '\164', '\150', '\040', '\061', '\012', '\152', + '\111', '\157', '\040', '\157', '\156', '\040', '\061', '\012', + '\152', '\121', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\106', '\171', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\143', '\160', '\125', '\040', '\143', '\150', + '\040', '\061', '\012', '\150', '\170', '\131', '\040', '\164', + '\150', '\040', '\061', '\012', '\161', '\142', '\104', '\040', + '\161', '\165', '\040', '\061', '\012', '\163', '\166', '\112', + '\040', '\163', '\164', '\040', '\061', '\012', '\166', '\152', + '\127', '\040', '\151', '\152', '\040', '\061', '\012', '\147', + '\160', '\131', '\040', '\156', '\147', '\040', '\061', '\012', + '\161', '\156', '\122', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\121', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\103', '\166', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\171', '\153', '\102', '\040', '\153', + '\141', '\040', '\061', '\012', '\170', '\147', '\102', '\040', + '\156', '\147', '\040', '\061', '\012', '\172', '\146', '\104', + '\040', '\163', '\172', '\040', '\061', '\012', '\171', '\110', + '\167', '\040', '\167', '\141', '\040', '\061', '\012', '\161', + '\144', '\107', '\040', '\161', '\165', '\040', '\061', '\012', + '\161', '\124', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\154', '\124', '\155', '\040', '\154', '\145', '\040', + '\061', '\012', '\152', '\147', '\102', '\040', '\156', '\147', + '\040', '\061', '\012', '\147', '\170', '\123', '\040', '\156', + '\147', '\040', '\061', '\012', '\161', '\120', '\145', '\040', + '\161', '\165', '\040', '\061', '\012', '\160', '\160', '\121', + '\040', '\160', '\162', '\040', '\061', '\012', '\171', '\170', + '\127', '\040', '\156', '\171', '\040', '\061', '\012', '\110', + '\152', '\153', '\040', '\151', '\152', '\040', '\061', '\012', + '\153', '\116', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\143', '\156', '\112', '\040', '\141', '\156', '\040', + '\061', '\012', '\165', '\110', '\144', '\040', '\161', '\165', + '\040', '\061', '\012', '\152', '\166', '\110', '\040', '\151', + '\152', '\040', '\061', '\012', '\107', '\147', '\156', '\040', + '\156', '\147', '\040', '\061', '\012', '\154', '\142', '\123', + '\040', '\154', '\145', '\040', '\061', '\012', '\121', '\143', + '\170', '\040', '\143', '\150', '\040', '\061', '\012', '\143', + '\161', '\122', '\040', '\143', '\150', '\040', '\061', '\012', + '\112', '\171', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\122', '\160', '\040', '\160', '\162', '\040', + '\061', '\012', '\156', '\146', '\101', '\040', '\141', '\156', + '\040', '\061', '\012', '\154', '\130', '\167', '\040', '\154', + '\145', '\040', '\061', '\012', '\143', '\155', '\112', '\040', + '\143', '\150', '\040', '\061', '\012', '\131', '\163', '\167', + '\040', '\163', '\164', '\040', '\061', '\012', '\161', '\121', + '\163', '\040', '\161', '\165', '\040', '\061', '\012', '\147', + '\163', '\130', '\040', '\156', '\147', '\040', '\061', '\012', + '\143', '\111', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\152', '\132', '\040', '\151', '\152', '\040', + '\061', '\012', '\114', '\154', '\142', '\040', '\154', '\145', + '\040', '\061', '\012', '\155', '\115', '\166', '\040', '\166', + '\141', '\040', '\061', '\012', '\154', '\126', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\106', '\160', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\132', '\155', + '\155', '\040', '\155', '\145', '\040', '\061', '\012', '\170', + '\115', '\144', '\040', '\144', '\145', '\040', '\061', '\012', + '\107', '\167', '\142', '\040', '\167', '\141', '\040', '\061', + '\012', '\121', '\152', '\166', '\040', '\151', '\152', '\040', + '\061', '\012', '\154', '\161', '\132', '\040', '\161', '\165', + '\040', '\061', '\012', '\172', '\112', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\127', '\153', '\171', '\040', + '\153', '\141', '\040', '\061', '\012', '\150', '\104', '\153', + '\040', '\164', '\150', '\040', '\061', '\012', '\171', '\114', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\144', + '\131', '\167', '\040', '\144', '\145', '\040', '\061', '\012', + '\144', '\103', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\107', '\155', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\170', '\124', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\167', '\153', '\106', '\040', '\153', + '\141', '\040', '\061', '\012', '\150', '\106', '\160', '\040', + '\164', '\150', '\040', '\061', '\012', '\161', '\156', '\102', + '\040', '\141', '\156', '\040', '\061', '\012', '\170', '\171', + '\112', '\040', '\156', '\171', '\040', '\061', '\012', '\156', + '\111', '\152', '\040', '\141', '\156', '\040', '\061', '\012', + '\170', '\131', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\127', '\161', '\162', '\040', '\161', '\165', '\040', + '\061', '\012', '\170', '\161', '\126', '\040', '\161', '\165', + '\040', '\061', '\012', '\167', '\131', '\153', '\040', '\153', + '\141', '\040', '\061', '\012', '\121', '\144', '\172', '\040', + '\144', '\145', '\040', '\061', '\012', '\146', '\142', '\116', + '\040', '\142', '\145', '\040', '\061', '\012', '\161', '\167', + '\131', '\040', '\161', '\165', '\040', '\061', '\012', '\125', + '\142', '\170', '\040', '\142', '\145', '\040', '\061', '\012', + '\167', '\164', '\114', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\121', '\167', '\040', '\141', '\156', '\040', + '\061', '\012', '\152', '\112', '\153', '\040', '\151', '\152', + '\040', '\061', '\012', '\116', '\172', '\163', '\040', '\163', + '\164', '\040', '\061', '\012', '\144', '\103', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\116', '\146', '\166', + '\040', '\166', '\141', '\040', '\061', '\012', '\110', '\147', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\110', + '\143', '\161', '\040', '\143', '\150', '\040', '\061', '\012', + '\130', '\166', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\163', '\170', '\112', '\040', '\163', '\164', '\040', + '\061', '\012', '\167', '\115', '\170', '\040', '\167', '\141', + '\040', '\061', '\012', '\161', '\106', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\107', '\172', '\146', '\040', + '\163', '\172', '\040', '\061', '\012', '\161', '\146', '\112', + '\040', '\161', '\165', '\040', '\061', '\012', '\172', '\144', + '\121', '\040', '\144', '\145', '\040', '\061', '\012', '\130', + '\147', '\172', '\040', '\156', '\147', '\040', '\061', '\012', + '\146', '\153', '\111', '\040', '\153', '\141', '\040', '\061', + '\012', '\160', '\166', '\113', '\040', '\166', '\141', '\040', + '\061', '\012', '\103', '\161', '\162', '\040', '\161', '\165', + '\040', '\061', '\012', '\172', '\106', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\157', '\110', '\155', '\040', + '\157', '\156', '\040', '\061', '\012', '\141', '\112', '\152', + '\040', '\141', '\156', '\040', '\061', '\012', '\106', '\172', + '\144', '\040', '\144', '\145', '\040', '\061', '\012', '\144', + '\127', '\153', '\040', '\144', '\145', '\040', '\061', '\012', + '\167', '\155', '\105', '\040', '\155', '\145', '\040', '\061', + '\012', '\163', '\115', '\154', '\040', '\154', '\145', '\040', + '\061', '\012', '\164', '\102', '\160', '\040', '\164', '\150', + '\040', '\061', '\012', '\166', '\116', '\167', '\040', '\166', + '\141', '\040', '\061', '\012', '\121', '\144', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\167', '\150', '\107', + '\040', '\164', '\150', '\040', '\061', '\012', '\161', '\101', + '\160', '\040', '\161', '\165', '\040', '\061', '\012', '\152', + '\162', '\115', '\040', '\145', '\162', '\040', '\061', '\012', + '\162', '\110', '\167', '\040', '\145', '\162', '\040', '\061', + '\012', '\114', '\166', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\147', '\122', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\171', '\152', '\126', '\040', '\151', + '\152', '\040', '\061', '\012', '\150', '\122', '\153', '\040', + '\164', '\150', '\040', '\061', '\012', '\142', '\153', '\126', + '\040', '\153', '\141', '\040', '\061', '\012', '\152', '\127', + '\155', '\040', '\151', '\152', '\040', '\061', '\012', '\171', + '\131', '\172', '\040', '\163', '\172', '\040', '\061', '\012', + '\166', '\124', '\171', '\040', '\166', '\141', '\040', '\061', + '\012', '\144', '\170', '\126', '\040', '\144', '\145', '\040', + '\061', '\012', '\155', '\113', '\171', '\040', '\155', '\145', + '\040', '\061', '\012', '\121', '\154', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\125', '\160', '\170', '\040', + '\160', '\162', '\040', '\061', '\012', '\121', '\160', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\114', '\167', + '\155', '\040', '\155', '\145', '\040', '\061', '\012', '\171', + '\130', '\162', '\040', '\145', '\162', '\040', '\061', '\012', + '\147', '\124', '\153', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\156', '\124', '\040', '\141', '\156', '\040', + '\061', '\012', '\126', '\154', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\121', '\161', '\144', '\040', '\161', + '\165', '\040', '\061', '\012', '\132', '\144', '\144', '\040', + '\144', '\145', '\040', '\061', '\012', '\130', '\161', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\104', '\146', + '\142', '\040', '\142', '\145', '\040', '\061', '\012', '\157', + '\145', '\117', '\040', '\157', '\156', '\040', '\061', '\012', + '\156', '\103', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\154', '\130', '\144', '\040', '\154', '\145', '\040', + '\061', '\012', '\166', '\110', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\166', '\101', '\142', '\040', '\166', + '\141', '\040', '\061', '\012', '\131', '\142', '\167', '\040', + '\167', '\141', '\040', '\061', '\012', '\172', '\104', '\156', + '\040', '\141', '\156', '\040', '\061', '\012', '\144', '\107', + '\153', '\040', '\144', '\145', '\040', '\061', '\012', '\160', + '\154', '\110', '\040', '\154', '\145', '\040', '\061', '\012', + '\154', '\170', '\107', '\040', '\154', '\145', '\040', '\061', + '\012', '\110', '\147', '\160', '\040', '\156', '\147', '\040', + '\061', '\012', '\152', '\122', '\172', '\040', '\151', '\152', + '\040', '\061', '\012', '\144', '\124', '\163', '\040', '\144', + '\145', '\040', '\061', '\012', '\155', '\103', '\152', '\040', + '\151', '\152', '\040', '\061', '\012', '\154', '\110', '\146', + '\040', '\154', '\145', '\040', '\061', '\012', '\154', '\114', + '\152', '\040', '\154', '\145', '\040', '\061', '\012', '\164', + '\116', '\142', '\040', '\164', '\150', '\040', '\061', '\012', + '\155', '\113', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\147', '\107', '\152', '\040', '\156', '\147', '\040', + '\061', '\012', '\152', '\154', '\121', '\040', '\154', '\145', + '\040', '\061', '\012', '\131', '\171', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\146', '\104', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\172', '\130', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\161', '\172', + '\132', '\040', '\161', '\165', '\040', '\061', '\012', '\146', + '\105', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\154', '\150', '\123', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\172', '\115', '\040', '\163', '\172', '\040', + '\061', '\012', '\170', '\161', '\124', '\040', '\161', '\165', + '\040', '\061', '\012', '\131', '\143', '\152', '\040', '\143', + '\150', '\040', '\061', '\012', '\146', '\142', '\106', '\040', + '\142', '\145', '\040', '\061', '\012', '\130', '\163', '\152', + '\040', '\151', '\152', '\040', '\061', '\012', '\114', '\156', + '\143', '\040', '\141', '\156', '\040', '\061', '\012', '\107', + '\161', '\160', '\040', '\161', '\165', '\040', '\061', '\012', + '\146', '\152', '\117', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\150', '\111', '\040', '\164', '\150', '\040', + '\061', '\012', '\172', '\147', '\110', '\040', '\156', '\147', + '\040', '\061', '\012', '\147', '\127', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\171', '\113', '\146', '\040', + '\156', '\171', '\040', '\061', '\012', '\165', '\121', '\144', + '\040', '\161', '\165', '\040', '\061', '\012', '\113', '\167', + '\154', '\040', '\154', '\145', '\040', '\061', '\012', '\144', + '\170', '\107', '\040', '\144', '\145', '\040', '\061', '\012', + '\131', '\161', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\113', '\143', '\040', '\164', '\150', '\040', + '\061', '\012', '\143', '\127', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\150', '\143', '\111', '\040', '\164', + '\150', '\040', '\061', '\012', '\167', '\146', '\131', '\040', + '\167', '\141', '\040', '\061', '\012', '\162', '\102', '\160', + '\040', '\145', '\162', '\040', '\061', '\012', '\143', '\112', + '\144', '\040', '\143', '\150', '\040', '\061', '\012', '\163', + '\131', '\146', '\040', '\163', '\172', '\040', '\061', '\012', + '\123', '\161', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\121', '\166', '\040', '\153', '\141', '\040', + '\061', '\012', '\170', '\160', '\106', '\040', '\160', '\162', + '\040', '\061', '\012', '\146', '\143', '\130', '\040', '\143', + '\150', '\040', '\061', '\012', '\171', '\146', '\113', '\040', + '\156', '\171', '\040', '\061', '\012', '\152', '\121', '\157', + '\040', '\157', '\156', '\040', '\061', '\012', '\147', '\124', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\121', + '\167', '\156', '\040', '\141', '\156', '\040', '\061', '\012', + '\120', '\156', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\132', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\167', '\120', '\172', '\040', '\163', '\172', + '\040', '\061', '\012', '\152', '\165', '\130', '\040', '\161', + '\165', '\040', '\061', '\012', '\114', '\170', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\151', '\130', '\162', + '\040', '\151', '\156', '\040', '\061', '\012', '\160', '\143', + '\105', '\040', '\143', '\150', '\040', '\061', '\012', '\116', + '\161', '\171', '\040', '\161', '\165', '\040', '\061', '\012', + '\150', '\152', '\111', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\172', '\126', '\040', '\164', '\150', '\040', + '\061', '\012', '\156', '\155', '\106', '\040', '\141', '\156', + '\040', '\061', '\012', '\160', '\166', '\127', '\040', '\166', + '\141', '\040', '\061', '\012', '\145', '\112', '\167', '\040', + '\145', '\162', '\040', '\061', '\012', '\111', '\161', '\144', + '\040', '\161', '\165', '\040', '\061', '\012', '\147', '\130', + '\171', '\040', '\156', '\147', '\040', '\061', '\012', '\167', + '\146', '\127', '\040', '\167', '\141', '\040', '\061', '\012', + '\126', '\144', '\167', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\112', '\170', '\040', '\161', '\165', '\040', + '\061', '\012', '\120', '\144', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\102', '\152', '\142', '\040', '\151', + '\152', '\040', '\061', '\012', '\161', '\114', '\154', '\040', + '\161', '\165', '\040', '\061', '\012', '\172', '\144', '\127', + '\040', '\144', '\145', '\040', '\061', '\012', '\146', '\121', + '\162', '\040', '\145', '\162', '\040', '\061', '\012', '\170', + '\172', '\127', '\040', '\163', '\172', '\040', '\061', '\012', + '\166', '\167', '\121', '\040', '\166', '\141', '\040', '\061', + '\012', '\162', '\167', '\125', '\040', '\145', '\162', '\040', + '\061', '\012', '\161', '\120', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\142', '\106', '\167', '\040', '\167', + '\141', '\040', '\061', '\012', '\166', '\110', '\154', '\040', + '\154', '\145', '\040', '\061', '\012', '\150', '\127', '\154', + '\040', '\164', '\150', '\040', '\061', '\012', '\167', '\147', + '\117', '\040', '\156', '\147', '\040', '\061', '\012', '\150', + '\114', '\153', '\040', '\164', '\150', '\040', '\061', '\012', + '\112', '\153', '\142', '\040', '\153', '\141', '\040', '\061', + '\012', '\172', '\102', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\104', '\150', '\170', '\040', '\164', '\150', + '\040', '\061', '\012', '\106', '\147', '\166', '\040', '\156', + '\147', '\040', '\061', '\012', '\142', '\160', '\101', '\040', + '\160', '\162', '\040', '\061', '\012', '\172', '\170', '\103', + '\040', '\163', '\172', '\040', '\061', '\012', '\147', '\146', + '\123', '\040', '\156', '\147', '\040', '\061', '\012', '\115', + '\166', '\170', '\040', '\166', '\141', '\040', '\061', '\012', + '\165', '\120', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\126', '\161', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\171', '\161', '\103', '\040', '\161', '\165', + '\040', '\061', '\012', '\166', '\115', '\153', '\040', '\153', + '\141', '\040', '\061', '\012', '\167', '\161', '\114', '\040', + '\161', '\165', '\040', '\061', '\012', '\167', '\162', '\112', + '\040', '\145', '\162', '\040', '\061', '\012', '\143', '\144', + '\116', '\040', '\143', '\150', '\040', '\061', '\012', '\160', + '\167', '\122', '\040', '\160', '\162', '\040', '\061', '\012', + '\150', '\115', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\120', '\146', '\040', '\151', '\152', '\040', + '\061', '\012', '\126', '\142', '\166', '\040', '\166', '\141', + '\040', '\061', '\012', '\161', '\172', '\106', '\040', '\161', + '\165', '\040', '\061', '\012', '\161', '\116', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\112', '\142', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\146', '\124', + '\153', '\040', '\153', '\141', '\040', '\061', '\012', '\132', + '\146', '\146', '\040', '\146', '\157', '\040', '\061', '\012', + '\106', '\172', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\113', '\143', '\167', '\040', '\143', '\150', '\040', + '\061', '\012', '\145', '\113', '\146', '\040', '\145', '\162', + '\040', '\061', '\012', '\160', '\161', '\132', '\040', '\161', + '\165', '\040', '\061', '\012', '\127', '\160', '\142', '\040', + '\160', '\162', '\040', '\061', '\012', '\152', '\153', '\106', + '\040', '\151', '\152', '\040', '\061', '\012', '\126', '\170', + '\160', '\040', '\160', '\162', '\040', '\061', '\012', '\150', + '\107', '\161', '\040', '\164', '\150', '\040', '\061', '\012', + '\161', '\102', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\143', '\124', '\040', '\143', '\150', '\040', + '\061', '\012', '\152', '\115', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\153', '\132', '\166', '\040', '\153', + '\141', '\040', '\061', '\012', '\161', '\153', '\107', '\040', + '\161', '\165', '\040', '\061', '\012', '\111', '\146', '\160', + '\040', '\160', '\162', '\040', '\061', '\012', '\144', '\122', + '\167', '\040', '\144', '\145', '\040', '\061', '\012', '\132', + '\154', '\152', '\040', '\154', '\145', '\040', '\061', '\012', + '\113', '\167', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\116', '\142', '\040', '\142', '\145', '\040', + '\061', '\012', '\144', '\131', '\171', '\040', '\144', '\145', + '\040', '\061', '\012', '\150', '\132', '\154', '\040', '\164', + '\150', '\040', '\061', '\012', '\167', '\164', '\120', '\040', + '\164', '\150', '\040', '\061', '\012', '\150', '\120', '\172', + '\040', '\164', '\150', '\040', '\061', '\012', '\131', '\153', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\112', + '\154', '\167', '\040', '\154', '\145', '\040', '\061', '\012', + '\152', '\116', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\162', '\127', '\040', '\145', '\162', '\040', + '\061', '\012', '\147', '\127', '\144', '\040', '\156', '\147', + '\040', '\061', '\012', '\171', '\130', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\146', '\121', '\154', '\040', + '\154', '\145', '\040', '\061', '\012', '\152', '\146', '\106', + '\040', '\151', '\152', '\040', '\061', '\012', '\105', '\152', + '\170', '\040', '\151', '\152', '\040', '\061', '\012', '\146', + '\107', '\153', '\040', '\153', '\141', '\040', '\061', '\012', + '\132', '\152', '\172', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\144', '\115', '\040', '\144', '\145', '\040', + '\061', '\012', '\152', '\154', '\106', '\040', '\154', '\145', + '\040', '\061', '\012', '\143', '\170', '\132', '\040', '\143', + '\150', '\040', '\061', '\012', '\132', '\147', '\153', '\040', + '\156', '\147', '\040', '\061', '\012', '\155', '\143', '\112', + '\040', '\143', '\150', '\040', '\061', '\012', '\163', '\154', + '\105', '\040', '\154', '\145', '\040', '\061', '\012', '\156', + '\131', '\161', '\040', '\141', '\156', '\040', '\061', '\012', + '\127', '\146', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\112', '\153', '\040', '\153', '\141', '\040', + '\061', '\012', '\142', '\166', '\106', '\040', '\166', '\141', + '\040', '\061', '\012', '\110', '\156', '\172', '\040', '\141', + '\156', '\040', '\061', '\012', '\127', '\153', '\166', '\040', + '\153', '\141', '\040', '\061', '\012', '\115', '\166', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\104', '\170', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\102', + '\166', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\163', '\115', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\122', '\146', '\040', '\167', '\141', '\040', + '\061', '\012', '\166', '\114', '\142', '\040', '\166', '\141', + '\040', '\061', '\012', '\172', '\107', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\155', '\106', '\160', '\040', + '\155', '\145', '\040', '\061', '\012', '\147', '\116', '\142', + '\040', '\156', '\147', '\040', '\061', '\012', '\160', '\103', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\170', + '\106', '\163', '\040', '\163', '\172', '\040', '\061', '\012', + '\152', '\113', '\146', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\112', '\142', '\040', '\161', '\165', '\040', + '\061', '\012', '\160', '\172', '\111', '\040', '\163', '\172', + '\040', '\061', '\012', '\152', '\147', '\107', '\040', '\156', + '\147', '\040', '\061', '\012', '\160', '\113', '\163', '\040', + '\163', '\172', '\040', '\061', '\012', '\146', '\161', '\104', + '\040', '\161', '\165', '\040', '\061', '\012', '\147', '\170', + '\121', '\040', '\156', '\147', '\040', '\061', '\012', '\146', + '\166', '\107', '\040', '\166', '\141', '\040', '\061', '\012', + '\167', '\147', '\106', '\040', '\156', '\147', '\040', '\061', + '\012', '\130', '\170', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\114', '\167', '\165', '\040', '\161', '\165', + '\040', '\061', '\012', '\144', '\154', '\130', '\040', '\154', + '\145', '\040', '\061', '\012', '\154', '\120', '\172', '\040', + '\154', '\145', '\040', '\061', '\012', '\127', '\161', '\153', + '\040', '\161', '\165', '\040', '\061', '\012', '\130', '\172', + '\152', '\040', '\151', '\152', '\040', '\061', '\012', '\165', + '\110', '\152', '\040', '\161', '\165', '\040', '\061', '\012', + '\165', '\106', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\166', '\126', '\040', '\151', '\152', '\040', + '\061', '\012', '\152', '\130', '\145', '\040', '\154', '\145', + '\040', '\061', '\012', '\132', '\146', '\155', '\040', '\155', + '\145', '\040', '\061', '\012', '\161', '\111', '\155', '\040', + '\161', '\165', '\040', '\061', '\012', '\172', '\142', '\102', + '\040', '\163', '\172', '\040', '\061', '\012', '\171', '\132', + '\146', '\040', '\156', '\171', '\040', '\061', '\012', '\163', + '\113', '\153', '\040', '\163', '\172', '\040', '\061', '\012', + '\172', '\160', '\114', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\113', '\147', '\040', '\161', '\165', '\040', + '\061', '\012', '\111', '\142', '\152', '\040', '\151', '\152', + '\040', '\061', '\012', '\151', '\121', '\142', '\040', '\151', + '\156', '\040', '\061', '\012', '\106', '\170', '\165', '\040', + '\161', '\165', '\040', '\061', '\012', '\106', '\160', '\142', + '\040', '\160', '\162', '\040', '\061', '\012', '\127', '\166', + '\141', '\040', '\141', '\156', '\040', '\061', '\012', '\146', + '\172', '\104', '\040', '\163', '\172', '\040', '\061', '\012', + '\142', '\153', '\124', '\040', '\153', '\141', '\040', '\061', + '\012', '\131', '\153', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\156', '\152', '\107', '\040', '\141', '\156', + '\040', '\061', '\012', '\125', '\166', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\147', '\146', '\124', '\040', + '\156', '\147', '\040', '\061', '\012', '\172', '\143', '\111', + '\040', '\143', '\150', '\040', '\061', '\012', '\142', '\104', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\112', + '\144', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\170', '\115', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\112', '\142', '\171', '\040', '\142', '\145', '\040', + '\061', '\012', '\154', '\167', '\112', '\040', '\154', '\145', + '\040', '\061', '\012', '\163', '\127', '\167', '\040', '\163', + '\172', '\040', '\061', '\012', '\123', '\166', '\167', '\040', + '\166', '\141', '\040', '\061', '\012', '\156', '\162', '\130', + '\040', '\141', '\156', '\040', '\061', '\012', '\165', '\166', + '\126', '\040', '\161', '\165', '\040', '\061', '\012', '\152', + '\126', '\162', '\040', '\145', '\162', '\040', '\061', '\012', + '\164', '\161', '\102', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\126', '\162', '\040', '\145', '\162', '\040', + '\061', '\012', '\153', '\121', '\154', '\040', '\154', '\145', + '\040', '\061', '\012', '\146', '\142', '\107', '\040', '\142', + '\145', '\040', '\061', '\012', '\162', '\161', '\115', '\040', + '\161', '\165', '\040', '\061', '\012', '\172', '\110', '\152', + '\040', '\151', '\152', '\040', '\061', '\012', '\146', '\150', + '\131', '\040', '\164', '\150', '\040', '\061', '\012', '\131', + '\172', '\162', '\040', '\145', '\162', '\040', '\061', '\012', + '\166', '\106', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\121', '\160', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\165', '\101', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\172', '\170', '\120', '\040', '\163', + '\172', '\040', '\061', '\012', '\152', '\103', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\161', '\141', '\115', + '\040', '\141', '\156', '\040', '\061', '\012', '\170', '\154', + '\131', '\040', '\154', '\145', '\040', '\061', '\012', '\143', + '\124', '\146', '\040', '\143', '\150', '\040', '\061', '\012', + '\153', '\102', '\146', '\040', '\153', '\141', '\040', '\061', + '\012', '\143', '\121', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\122', '\142', '\152', '\040', '\151', '\152', + '\040', '\061', '\012', '\153', '\126', '\163', '\040', '\163', + '\172', '\040', '\061', '\012', '\142', '\107', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\167', '\144', '\116', + '\040', '\144', '\145', '\040', '\061', '\012', '\147', '\146', + '\116', '\040', '\156', '\147', '\040', '\061', '\012', '\142', + '\120', '\152', '\040', '\151', '\152', '\040', '\061', '\012', + '\147', '\143', '\111', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\170', '\152', '\040', '\156', '\147', '\040', + '\061', '\012', '\162', '\110', '\142', '\040', '\145', '\162', + '\040', '\061', '\012', '\160', '\126', '\162', '\040', '\145', + '\162', '\040', '\061', '\012', '\162', '\126', '\152', '\040', + '\145', '\162', '\040', '\061', '\012', '\166', '\147', '\123', + '\040', '\156', '\147', '\040', '\061', '\012', '\106', '\161', + '\172', '\040', '\161', '\165', '\040', '\061', '\012', '\170', + '\115', '\153', '\040', '\153', '\141', '\040', '\061', '\012', + '\161', '\121', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\132', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\152', '\102', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\165', '\167', '\131', '\040', '\161', + '\165', '\040', '\061', '\012', '\162', '\110', '\146', '\040', + '\145', '\162', '\040', '\061', '\012', '\143', '\172', '\130', + '\040', '\143', '\150', '\040', '\061', '\012', '\172', '\143', + '\124', '\040', '\143', '\150', '\040', '\061', '\012', '\142', + '\106', '\152', '\040', '\151', '\152', '\040', '\061', '\012', + '\161', '\143', '\102', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\146', '\124', '\040', '\164', '\150', '\040', + '\061', '\012', '\170', '\161', '\117', '\040', '\161', '\165', + '\040', '\061', '\012', '\161', '\146', '\160', '\040', '\161', + '\165', '\040', '\061', '\012', '\170', '\152', '\125', '\040', + '\151', '\152', '\040', '\061', '\012', '\142', '\150', '\122', + '\040', '\164', '\150', '\040', '\061', '\012', '\164', '\127', + '\166', '\040', '\164', '\150', '\040', '\061', '\012', '\151', + '\161', '\105', '\040', '\151', '\156', '\040', '\061', '\012', + '\147', '\160', '\125', '\040', '\156', '\147', '\040', '\061', + '\012', '\151', '\127', '\142', '\040', '\151', '\156', '\040', + '\061', '\012', '\164', '\154', '\120', '\040', '\164', '\150', + '\040', '\061', '\012', '\164', '\131', '\161', '\040', '\164', + '\150', '\040', '\061', '\012', '\142', '\103', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\157', '\113', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\123', '\147', + '\152', '\040', '\156', '\147', '\040', '\061', '\012', '\150', + '\166', '\161', '\040', '\164', '\150', '\040', '\061', '\012', + '\153', '\146', '\131', '\040', '\153', '\141', '\040', '\061', + '\012', '\172', '\142', '\115', '\040', '\163', '\172', '\040', + '\061', '\012', '\172', '\166', '\101', '\040', '\163', '\172', + '\040', '\061', '\012', '\143', '\110', '\160', '\040', '\143', + '\150', '\040', '\061', '\012', '\166', '\166', '\113', '\040', + '\166', '\141', '\040', '\061', '\012', '\146', '\160', '\132', + '\040', '\160', '\162', '\040', '\061', '\012', '\144', '\146', + '\130', '\040', '\144', '\145', '\040', '\061', '\012', '\167', + '\162', '\113', '\040', '\145', '\162', '\040', '\061', '\012', + '\170', '\145', '\105', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\153', '\131', '\040', '\153', '\141', '\040', + '\061', '\012', '\163', '\142', '\130', '\040', '\163', '\172', + '\040', '\061', '\012', '\146', '\143', '\123', '\040', '\143', + '\150', '\040', '\061', '\012', '\166', '\113', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\121', '\154', '\170', + '\040', '\154', '\145', '\040', '\061', '\012', '\132', '\161', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\161', + '\127', '\147', '\040', '\161', '\165', '\040', '\061', '\012', + '\143', '\144', '\114', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\166', '\107', '\040', '\151', '\152', '\040', + '\061', '\012', '\115', '\147', '\170', '\040', '\156', '\147', + '\040', '\061', '\012', '\147', '\167', '\106', '\040', '\156', + '\147', '\040', '\061', '\012', '\153', '\144', '\120', '\040', + '\144', '\145', '\040', '\061', '\012', '\165', '\115', '\162', + '\040', '\161', '\165', '\040', '\061', '\012', '\164', '\143', + '\104', '\040', '\164', '\150', '\040', '\061', '\012', '\161', + '\162', '\114', '\040', '\161', '\165', '\040', '\061', '\012', + '\115', '\164', '\155', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\121', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\110', '\160', '\170', '\040', '\160', '\162', + '\040', '\061', '\012', '\172', '\160', '\111', '\040', '\163', + '\172', '\040', '\061', '\012', '\152', '\153', '\122', '\040', + '\151', '\152', '\040', '\061', '\012', '\153', '\150', '\110', + '\040', '\164', '\150', '\040', '\061', '\012', '\155', '\123', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\160', + '\106', '\172', '\040', '\163', '\172', '\040', '\061', '\012', + '\152', '\165', '\117', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\171', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\152', '\107', '\144', '\040', '\144', '\145', + '\040', '\061', '\012', '\131', '\172', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\167', '\142', '\103', '\040', + '\167', '\141', '\040', '\061', '\012', '\167', '\123', '\142', + '\040', '\167', '\141', '\040', '\061', '\012', '\163', '\132', + '\144', '\040', '\144', '\145', '\040', '\061', '\012', '\122', + '\172', '\170', '\040', '\163', '\172', '\040', '\061', '\012', + '\106', '\154', '\170', '\040', '\154', '\145', '\040', '\061', + '\012', '\142', '\161', '\103', '\040', '\161', '\165', '\040', + '\061', '\012', '\154', '\143', '\110', '\040', '\143', '\150', + '\040', '\061', '\012', '\167', '\155', '\107', '\040', '\155', + '\145', '\040', '\061', '\012', '\172', '\103', '\152', '\040', + '\151', '\152', '\040', '\061', '\012', '\170', '\141', '\104', + '\040', '\141', '\156', '\040', '\061', '\012', '\151', '\167', + '\110', '\040', '\151', '\156', '\040', '\061', '\012', '\161', + '\104', '\160', '\040', '\161', '\165', '\040', '\061', '\012', + '\163', '\107', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\130', '\150', '\171', '\040', '\164', '\150', '\040', + '\061', '\012', '\145', '\126', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\167', '\153', '\112', '\040', '\167', + '\141', '\040', '\061', '\012', '\114', '\143', '\146', '\040', + '\143', '\150', '\040', '\061', '\012', '\154', '\147', '\121', + '\040', '\156', '\147', '\040', '\061', '\012', '\104', '\150', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\172', + '\146', '\117', '\040', '\163', '\172', '\040', '\061', '\012', + '\153', '\126', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\155', '\114', '\040', '\164', '\150', '\040', + '\061', '\012', '\117', '\167', '\146', '\040', '\167', '\141', + '\040', '\061', '\012', '\167', '\132', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\144', '\156', '\116', '\040', + '\141', '\156', '\040', '\061', '\012', '\115', '\172', '\160', + '\040', '\163', '\172', '\040', '\061', '\012', '\155', '\131', + '\167', '\040', '\155', '\145', '\040', '\061', '\012', '\171', + '\114', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\130', '\170', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\167', '\111', '\040', '\161', '\165', '\040', + '\061', '\012', '\124', '\170', '\163', '\040', '\163', '\172', + '\040', '\061', '\012', '\171', '\113', '\160', '\040', '\160', + '\162', '\040', '\061', '\012', '\142', '\152', '\130', '\040', + '\151', '\152', '\040', '\061', '\012', '\160', '\142', '\123', + '\040', '\160', '\162', '\040', '\061', '\012', '\172', '\162', + '\120', '\040', '\145', '\162', '\040', '\061', '\012', '\150', + '\112', '\155', '\040', '\164', '\150', '\040', '\061', '\012', + '\161', '\147', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\167', '\131', '\040', '\163', '\172', '\040', + '\061', '\012', '\162', '\130', '\153', '\040', '\145', '\162', + '\040', '\061', '\012', '\156', '\104', '\170', '\040', '\141', + '\156', '\040', '\061', '\012', '\166', '\107', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\155', '\121', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\165', '\160', + '\131', '\040', '\161', '\165', '\040', '\061', '\012', '\162', + '\114', '\156', '\040', '\141', '\156', '\040', '\061', '\012', + '\126', '\146', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\167', '\103', '\166', '\040', '\166', '\141', '\040', + '\061', '\012', '\143', '\147', '\170', '\040', '\143', '\150', + '\040', '\061', '\012', '\153', '\132', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\127', '\152', '\167', '\040', + '\151', '\152', '\040', '\061', '\012', '\121', '\141', '\170', + '\040', '\141', '\156', '\040', '\061', '\012', '\147', '\162', + '\107', '\040', '\156', '\147', '\040', '\061', '\012', '\142', + '\112', '\144', '\040', '\144', '\145', '\040', '\061', '\012', + '\144', '\112', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\143', '\115', '\144', '\040', '\143', '\150', '\040', + '\061', '\012', '\121', '\143', '\163', '\040', '\143', '\150', + '\040', '\061', '\012', '\155', '\153', '\113', '\040', '\153', + '\141', '\040', '\061', '\012', '\152', '\116', '\170', '\040', + '\151', '\152', '\040', '\061', '\012', '\155', '\162', '\131', + '\040', '\145', '\162', '\040', '\061', '\012', '\130', '\167', + '\170', '\040', '\167', '\141', '\040', '\061', '\012', '\162', + '\132', '\154', '\040', '\145', '\162', '\040', '\061', '\012', + '\147', '\170', '\125', '\040', '\156', '\147', '\040', '\061', + '\012', '\114', '\156', '\166', '\040', '\141', '\156', '\040', + '\061', '\012', '\171', '\147', '\103', '\040', '\156', '\147', + '\040', '\061', '\012', '\104', '\161', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\154', '\114', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\155', '\156', '\121', + '\040', '\141', '\156', '\040', '\061', '\012', '\153', '\152', + '\125', '\040', '\151', '\152', '\040', '\061', '\012', '\142', + '\166', '\117', '\040', '\166', '\141', '\040', '\061', '\012', + '\157', '\126', '\155', '\040', '\157', '\156', '\040', '\061', + '\012', '\166', '\127', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\162', '\107', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\164', '\142', '\112', '\040', '\164', + '\150', '\040', '\061', '\012', '\146', '\123', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\167', '\112', '\156', + '\040', '\141', '\156', '\040', '\061', '\012', '\146', '\112', + '\166', '\040', '\166', '\141', '\040', '\061', '\012', '\157', + '\121', '\166', '\040', '\157', '\156', '\040', '\061', '\012', + '\126', '\167', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\160', '\156', '\125', '\040', '\141', '\156', '\040', + '\061', '\012', '\116', '\155', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\143', '\124', '\161', '\040', '\143', + '\150', '\040', '\061', '\012', '\105', '\144', '\170', '\040', + '\144', '\145', '\040', '\061', '\012', '\165', '\161', '\167', + '\040', '\161', '\165', '\040', '\061', '\012', '\131', '\162', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\121', + '\156', '\170', '\040', '\141', '\156', '\040', '\061', '\012', + '\155', '\112', '\146', '\040', '\155', '\145', '\040', '\061', + '\012', '\153', '\104', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\130', '\150', '\144', '\040', '\164', '\150', + '\040', '\061', '\012', '\156', '\114', '\170', '\040', '\141', + '\156', '\040', '\061', '\012', '\170', '\153', '\125', '\040', + '\153', '\141', '\040', '\061', '\012', '\146', '\161', '\124', + '\040', '\161', '\165', '\040', '\061', '\012', '\161', '\131', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\142', + '\106', '\166', '\040', '\166', '\141', '\040', '\061', '\012', + '\170', '\142', '\121', '\040', '\142', '\145', '\040', '\061', + '\012', '\166', '\143', '\123', '\040', '\143', '\150', '\040', + '\061', '\012', '\161', '\161', '\124', '\040', '\161', '\165', + '\040', '\061', '\012', '\147', '\153', '\106', '\040', '\156', + '\147', '\040', '\061', '\012', '\172', '\106', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\153', '\160', '\105', + '\040', '\153', '\141', '\040', '\061', '\012', '\107', '\170', + '\142', '\040', '\142', '\145', '\040', '\061', '\012', '\132', + '\164', '\167', '\040', '\164', '\150', '\040', '\061', '\012', + '\161', '\111', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\153', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\167', '\144', '\126', '\040', '\144', '\145', + '\040', '\061', '\012', '\162', '\167', '\120', '\040', '\145', + '\162', '\040', '\061', '\012', '\141', '\103', '\147', '\040', + '\141', '\156', '\040', '\061', '\012', '\132', '\162', '\163', + '\040', '\145', '\162', '\040', '\061', '\012', '\172', '\155', + '\127', '\040', '\163', '\172', '\040', '\061', '\012', '\166', + '\146', '\117', '\040', '\166', '\141', '\040', '\061', '\012', + '\150', '\102', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\142', '\110', '\040', '\164', '\150', '\040', + '\061', '\012', '\104', '\170', '\166', '\040', '\166', '\141', + '\040', '\061', '\012', '\172', '\144', '\104', '\040', '\144', + '\145', '\040', '\061', '\012', '\156', '\102', '\167', '\040', + '\141', '\156', '\040', '\061', '\012', '\154', '\162', '\126', + '\040', '\145', '\162', '\040', '\061', '\012', '\147', '\121', + '\161', '\040', '\156', '\147', '\040', '\061', '\012', '\164', + '\154', '\113', '\040', '\164', '\150', '\040', '\061', '\012', + '\172', '\164', '\120', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\161', '\126', '\040', '\161', '\165', '\040', + '\061', '\012', '\156', '\122', '\155', '\040', '\141', '\156', + '\040', '\061', '\012', '\152', '\126', '\172', '\040', '\163', + '\172', '\040', '\061', '\012', '\103', '\162', '\161', '\040', + '\145', '\162', '\040', '\061', '\012', '\146', '\106', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\130', '\152', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\103', + '\155', '\154', '\040', '\154', '\145', '\040', '\061', '\012', + '\161', '\127', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\172', '\117', '\040', '\151', '\152', '\040', + '\061', '\012', '\115', '\144', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\155', '\164', '\121', '\040', '\164', + '\150', '\040', '\061', '\012', '\162', '\107', '\166', '\040', + '\145', '\162', '\040', '\061', '\012', '\153', '\107', '\156', + '\040', '\141', '\156', '\040', '\061', '\012', '\155', '\114', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\165', + '\127', '\152', '\040', '\161', '\165', '\040', '\061', '\012', + '\122', '\143', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\126', '\160', '\040', '\143', '\150', '\040', + '\061', '\012', '\142', '\127', '\153', '\040', '\153', '\141', + '\040', '\061', '\012', '\130', '\172', '\170', '\040', '\163', + '\172', '\040', '\061', '\012', '\127', '\153', '\142', '\040', + '\153', '\141', '\040', '\061', '\012', '\170', '\172', '\110', + '\040', '\163', '\172', '\040', '\061', '\012', '\161', '\165', + '\120', '\040', '\165', '\156', '\040', '\061', '\012', '\144', + '\110', '\166', '\040', '\144', '\145', '\040', '\061', '\012', + '\104', '\155', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\104', '\147', '\166', '\040', '\156', '\147', '\040', + '\061', '\012', '\164', '\147', '\131', '\040', '\164', '\150', + '\040', '\061', '\012', '\152', '\164', '\115', '\040', '\164', + '\150', '\040', '\061', '\012', '\164', '\115', '\172', '\040', + '\164', '\150', '\040', '\061', '\012', '\142', '\110', '\155', + '\040', '\155', '\145', '\040', '\061', '\012', '\132', '\146', + '\153', '\040', '\153', '\141', '\040', '\061', '\012', '\170', + '\132', '\160', '\040', '\160', '\162', '\040', '\061', '\012', + '\152', '\153', '\110', '\040', '\151', '\152', '\040', '\061', + '\012', '\162', '\116', '\160', '\040', '\145', '\162', '\040', + '\061', '\012', '\170', '\115', '\166', '\040', '\166', '\141', + '\040', '\061', '\012', '\167', '\160', '\106', '\040', '\160', + '\162', '\040', '\061', '\012', '\144', '\152', '\104', '\040', + '\144', '\145', '\040', '\061', '\012', '\142', '\170', '\126', + '\040', '\142', '\145', '\040', '\061', '\012', '\150', '\147', + '\123', '\040', '\164', '\150', '\040', '\061', '\012', '\120', + '\153', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\104', '\170', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\115', '\170', '\040', '\155', '\145', '\040', + '\061', '\012', '\144', '\107', '\152', '\040', '\144', '\145', + '\040', '\061', '\012', '\153', '\142', '\110', '\040', '\153', + '\141', '\040', '\061', '\012', '\114', '\150', '\147', '\040', + '\164', '\150', '\040', '\061', '\012', '\104', '\166', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\161', '\162', + '\124', '\040', '\161', '\165', '\040', '\061', '\012', '\111', + '\152', '\167', '\040', '\151', '\152', '\040', '\061', '\012', + '\167', '\165', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\167', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\144', '\150', '\112', '\040', '\164', '\150', + '\040', '\061', '\012', '\161', '\143', '\122', '\040', '\143', + '\150', '\040', '\061', '\012', '\167', '\150', '\115', '\040', + '\164', '\150', '\040', '\061', '\012', '\160', '\147', '\120', + '\040', '\156', '\147', '\040', '\061', '\012', '\161', '\153', + '\122', '\040', '\161', '\165', '\040', '\061', '\012', '\163', + '\161', '\122', '\040', '\161', '\165', '\040', '\061', '\012', + '\154', '\170', '\131', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\126', '\167', '\040', '\166', '\141', '\040', + '\061', '\012', '\154', '\113', '\144', '\040', '\154', '\145', + '\040', '\061', '\012', '\116', '\154', '\171', '\040', '\154', + '\145', '\040', '\061', '\012', '\171', '\113', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\161', '\102', '\142', + '\040', '\161', '\165', '\040', '\061', '\012', '\167', '\121', + '\170', '\040', '\167', '\141', '\040', '\061', '\012', '\153', + '\131', '\167', '\040', '\153', '\141', '\040', '\061', '\012', + '\146', '\121', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\163', '\166', '\127', '\040', '\163', '\172', '\040', + '\061', '\012', '\171', '\107', '\160', '\040', '\160', '\162', + '\040', '\061', '\012', '\171', '\164', '\102', '\040', '\164', + '\150', '\040', '\061', '\012', '\152', '\166', '\125', '\040', + '\151', '\152', '\040', '\061', '\012', '\153', '\152', '\172', + '\040', '\153', '\141', '\040', '\061', '\012', '\152', '\126', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\121', + '\142', '\172', '\040', '\163', '\172', '\040', '\061', '\012', + '\160', '\161', '\115', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\167', '\165', '\040', '\153', '\165', '\040', + '\061', '\012', '\121', '\167', '\167', '\040', '\167', '\141', + '\040', '\061', '\012', '\144', '\143', '\132', '\040', '\143', + '\150', '\040', '\061', '\012', '\154', '\150', '\107', '\040', + '\164', '\150', '\040', '\061', '\012', '\147', '\155', '\123', + '\040', '\156', '\147', '\040', '\061', '\012', '\111', '\161', + '\172', '\040', '\161', '\165', '\040', '\061', '\012', '\172', + '\132', '\146', '\040', '\163', '\172', '\040', '\061', '\012', + '\150', '\114', '\156', '\040', '\164', '\150', '\040', '\061', + '\012', '\145', '\115', '\146', '\040', '\145', '\162', '\040', + '\061', '\012', '\170', '\116', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\155', '\120', '\155', '\040', '\165', + '\155', '\040', '\061', '\012', '\160', '\115', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\167', '\172', '\127', + '\040', '\163', '\172', '\040', '\061', '\012', '\153', '\122', + '\154', '\040', '\154', '\145', '\040', '\061', '\012', '\150', + '\172', '\113', '\040', '\164', '\150', '\040', '\061', '\012', + '\146', '\142', '\117', '\040', '\142', '\145', '\040', '\061', + '\012', '\130', '\170', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\106', '\156', '\170', '\040', '\141', '\156', + '\040', '\061', '\012', '\102', '\166', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\142', '\152', '\132', '\040', + '\151', '\152', '\040', '\061', '\012', '\164', '\143', '\131', + '\040', '\164', '\150', '\040', '\061', '\012', '\144', '\155', + '\102', '\040', '\144', '\145', '\040', '\061', '\012', '\161', + '\106', '\145', '\040', '\161', '\165', '\040', '\061', '\012', + '\153', '\170', '\102', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\102', '\172', '\040', '\161', '\165', '\040', + '\061', '\012', '\160', '\126', '\160', '\040', '\160', '\162', + '\040', '\061', '\012', '\142', '\157', '\121', '\040', '\157', + '\156', '\040', '\061', '\012', '\170', '\157', '\110', '\040', + '\157', '\156', '\040', '\061', '\012', '\144', '\127', '\147', + '\040', '\144', '\145', '\040', '\061', '\012', '\124', '\144', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\172', + '\116', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\166', '\131', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\160', '\104', '\146', '\040', '\160', '\162', '\040', + '\061', '\012', '\154', '\167', '\107', '\040', '\154', '\145', + '\040', '\061', '\012', '\150', '\104', '\161', '\040', '\164', + '\150', '\040', '\061', '\012', '\112', '\144', '\171', '\040', + '\144', '\145', '\040', '\061', '\012', '\163', '\156', '\132', + '\040', '\141', '\156', '\040', '\061', '\012', '\155', '\172', + '\125', '\040', '\163', '\172', '\040', '\061', '\012', '\172', + '\113', '\170', '\040', '\163', '\172', '\040', '\061', '\012', + '\162', '\166', '\103', '\040', '\145', '\162', '\040', '\061', + '\012', '\167', '\165', '\123', '\040', '\161', '\165', '\040', + '\061', '\012', '\144', '\156', '\121', '\040', '\141', '\156', + '\040', '\061', '\012', '\166', '\103', '\171', '\040', '\166', + '\141', '\040', '\061', '\012', '\125', '\144', '\167', '\040', + '\167', '\141', '\040', '\061', '\012', '\142', '\124', '\154', + '\040', '\154', '\145', '\040', '\061', '\012', '\161', '\142', + '\103', '\040', '\161', '\165', '\040', '\061', '\012', '\164', + '\142', '\124', '\040', '\164', '\150', '\040', '\061', '\012', + '\151', '\104', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\127', '\150', '\142', '\040', '\164', '\150', '\040', + '\061', '\012', '\164', '\142', '\130', '\040', '\164', '\150', + '\040', '\061', '\012', '\164', '\146', '\117', '\040', '\164', + '\150', '\040', '\061', '\012', '\124', '\146', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\144', '\142', '\127', + '\040', '\144', '\145', '\040', '\061', '\012', '\102', '\144', + '\171', '\040', '\144', '\145', '\040', '\061', '\012', '\166', + '\152', '\122', '\040', '\151', '\152', '\040', '\061', '\012', + '\143', '\142', '\103', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\165', '\127', '\040', '\161', '\165', '\040', + '\061', '\012', '\167', '\103', '\167', '\040', '\167', '\141', + '\040', '\061', '\012', '\127', '\144', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\166', '\122', '\142', '\040', + '\166', '\141', '\040', '\061', '\012', '\142', '\127', '\155', + '\040', '\155', '\145', '\040', '\061', '\012', '\166', '\132', + '\167', '\040', '\166', '\141', '\040', '\061', '\012', '\144', + '\112', '\152', '\040', '\144', '\145', '\040', '\061', '\012', + '\161', '\132', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\112', '\147', '\161', '\040', '\156', '\147', '\040', + '\061', '\012', '\172', '\142', '\110', '\040', '\163', '\172', + '\040', '\061', '\012', '\150', '\112', '\154', '\040', '\164', + '\150', '\040', '\061', '\012', '\130', '\150', '\147', '\040', + '\164', '\150', '\040', '\061', '\012', '\156', '\126', '\160', + '\040', '\141', '\156', '\040', '\061', '\012', '\144', '\126', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\161', + '\103', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\157', '\131', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\167', '\110', '\040', '\153', '\141', '\040', + '\061', '\012', '\166', '\167', '\116', '\040', '\166', '\141', + '\040', '\061', '\012', '\172', '\146', '\167', '\040', '\163', + '\172', '\040', '\061', '\012', '\166', '\154', '\117', '\040', + '\154', '\145', '\040', '\061', '\012', '\172', '\164', '\130', + '\040', '\164', '\151', '\040', '\061', '\012', '\144', '\113', + '\170', '\040', '\144', '\145', '\040', '\061', '\012', '\170', + '\121', '\163', '\040', '\163', '\172', '\040', '\061', '\012', + '\143', '\104', '\154', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\126', '\166', '\040', '\166', '\141', '\040', + '\061', '\012', '\172', '\160', '\116', '\040', '\163', '\172', + '\040', '\061', '\012', '\170', '\153', '\107', '\040', '\153', + '\141', '\040', '\061', '\012', '\145', '\161', '\127', '\040', + '\161', '\165', '\040', '\061', '\012', '\152', '\144', '\104', + '\040', '\144', '\151', '\040', '\061', '\012', '\146', '\121', + '\155', '\040', '\155', '\145', '\040', '\061', '\012', '\131', + '\150', '\154', '\040', '\164', '\150', '\040', '\061', '\012', + '\164', '\102', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\105', '\146', '\040', '\161', '\165', '\040', + '\061', '\012', '\167', '\150', '\130', '\040', '\164', '\150', + '\040', '\061', '\012', '\126', '\147', '\166', '\040', '\156', + '\147', '\040', '\061', '\012', '\114', '\163', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\144', '\146', '\112', + '\040', '\144', '\145', '\040', '\061', '\012', '\132', '\144', + '\160', '\040', '\144', '\145', '\040', '\061', '\012', '\162', + '\132', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\164', '\132', '\150', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\164', '\103', '\040', '\164', '\150', '\040', + '\061', '\012', '\172', '\170', '\121', '\040', '\163', '\172', + '\040', '\061', '\012', '\126', '\156', '\152', '\040', '\141', + '\156', '\040', '\061', '\012', '\163', '\110', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\167', '\131', '\154', + '\040', '\154', '\145', '\040', '\061', '\012', '\102', '\161', + '\142', '\040', '\161', '\165', '\040', '\061', '\012', '\171', + '\162', '\126', '\040', '\145', '\162', '\040', '\061', '\012', + '\131', '\143', '\163', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\122', '\167', '\040', '\151', '\152', '\040', + '\061', '\012', '\151', '\127', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\150', '\126', '\167', '\040', '\164', + '\150', '\040', '\061', '\012', '\167', '\132', '\163', '\040', + '\163', '\172', '\040', '\061', '\012', '\103', '\161', '\157', + '\040', '\161', '\165', '\040', '\061', '\012', '\107', '\146', + '\156', '\040', '\141', '\156', '\040', '\061', '\012', '\162', + '\102', '\166', '\040', '\145', '\162', '\040', '\061', '\012', + '\117', '\152', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\107', '\146', '\040', '\163', '\172', '\040', + '\061', '\012', '\142', '\132', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\106', '\166', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\132', '\147', '\163', '\040', + '\156', '\147', '\040', '\061', '\012', '\122', '\146', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\122', '\167', + '\167', '\040', '\167', '\141', '\040', '\061', '\012', '\131', + '\162', '\160', '\040', '\145', '\162', '\040', '\061', '\012', + '\151', '\106', '\160', '\040', '\151', '\156', '\040', '\061', + '\012', '\142', '\126', '\170', '\040', '\142', '\145', '\040', + '\061', '\012', '\172', '\146', '\115', '\040', '\163', '\172', + '\040', '\061', '\012', '\161', '\144', '\126', '\040', '\161', + '\165', '\040', '\061', '\012', '\142', '\107', '\155', '\040', + '\155', '\145', '\040', '\061', '\012', '\164', '\156', '\112', + '\040', '\164', '\150', '\040', '\061', '\012', '\160', '\144', + '\122', '\040', '\144', '\145', '\040', '\061', '\012', '\147', + '\102', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\147', '\172', '\103', '\040', '\156', '\147', '\040', '\061', + '\012', '\120', '\167', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\165', '\101', '\167', '\040', '\161', '\165', + '\040', '\061', '\012', '\172', '\156', '\130', '\040', '\141', + '\156', '\040', '\061', '\012', '\166', '\147', '\124', '\040', + '\156', '\147', '\040', '\061', '\012', '\157', '\101', '\167', + '\040', '\153', '\157', '\040', '\061', '\012', '\170', '\102', + '\155', '\040', '\155', '\145', '\040', '\061', '\012', '\144', + '\116', '\146', '\040', '\144', '\145', '\040', '\061', '\012', + '\120', '\161', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\116', '\160', '\144', '\040', '\144', '\151', '\040', + '\061', '\012', '\157', '\125', '\171', '\040', '\153', '\157', + '\040', '\061', '\012', '\146', '\160', '\104', '\040', '\160', + '\162', '\040', '\061', '\012', '\122', '\146', '\170', '\040', + '\146', '\157', '\040', '\061', '\012', '\154', '\130', '\155', + '\040', '\154', '\145', '\040', '\061', '\012', '\161', '\127', + '\163', '\040', '\161', '\165', '\040', '\061', '\012', '\147', + '\127', '\166', '\040', '\166', '\151', '\040', '\061', '\012', + '\106', '\167', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\114', '\161', '\152', '\040', '\161', '\165', '\040', + '\061', '\012', '\146', '\166', '\121', '\040', '\166', '\141', + '\040', '\061', '\012', '\172', '\147', '\102', '\040', '\156', + '\147', '\040', '\061', '\012', '\153', '\112', '\154', '\040', + '\154', '\145', '\040', '\061', '\012', '\166', '\127', '\157', + '\040', '\157', '\156', '\040', '\061', '\012', '\130', '\166', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\171', + '\104', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\142', '\144', '\120', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\126', '\146', '\040', '\151', '\152', '\040', + '\061', '\012', '\167', '\120', '\167', '\040', '\167', '\141', + '\040', '\061', '\012', '\144', '\167', '\101', '\040', '\144', + '\145', '\040', '\061', '\012', '\117', '\161', '\160', '\040', + '\161', '\165', '\040', '\061', '\012', '\161', '\151', '\132', + '\040', '\151', '\156', '\040', '\061', '\012', '\170', '\144', + '\126', '\040', '\144', '\145', '\040', '\061', '\012', '\161', + '\106', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\161', '\172', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\167', '\114', '\040', '\167', '\141', '\040', + '\061', '\012', '\163', '\127', '\166', '\040', '\163', '\172', + '\040', '\061', '\012', '\124', '\160', '\171', '\040', '\160', + '\162', '\040', '\061', '\012', '\167', '\142', '\146', '\040', + '\167', '\141', '\040', '\061', '\012', '\165', '\120', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\113', '\156', + '\167', '\040', '\141', '\156', '\040', '\061', '\012', '\151', + '\165', '\117', '\040', '\151', '\156', '\040', '\061', '\012', + '\121', '\144', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\131', '\146', '\166', '\040', '\166', '\141', '\040', + '\061', '\012', '\167', '\165', '\113', '\040', '\161', '\165', + '\040', '\061', '\012', '\170', '\114', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\171', '\112', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\116', '\146', '\153', + '\040', '\153', '\141', '\040', '\061', '\012', '\131', '\161', + '\154', '\040', '\161', '\165', '\040', '\061', '\012', '\161', + '\163', '\110', '\040', '\161', '\165', '\040', '\061', '\012', + '\122', '\172', '\166', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\111', '\160', '\040', '\160', '\162', '\040', + '\061', '\012', '\163', '\121', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\164', '\147', '\103', '\040', '\164', + '\150', '\040', '\061', '\012', '\161', '\123', '\141', '\040', + '\141', '\156', '\040', '\061', '\012', '\146', '\170', '\121', + '\040', '\146', '\157', '\040', '\061', '\012', '\150', '\143', + '\132', '\040', '\164', '\150', '\040', '\061', '\012', '\167', + '\142', '\112', '\040', '\167', '\141', '\040', '\061', '\012', + '\161', '\122', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\107', '\143', '\171', '\040', '\143', '\150', '\040', + '\061', '\012', '\166', '\132', '\155', '\040', '\166', '\141', + '\040', '\061', '\012', '\130', '\172', '\154', '\040', '\154', + '\145', '\040', '\061', '\012', '\167', '\147', '\122', '\040', + '\156', '\147', '\040', '\061', '\012', '\144', '\154', '\117', + '\040', '\154', '\145', '\040', '\061', '\012', '\164', '\103', + '\142', '\040', '\164', '\150', '\040', '\061', '\012', '\161', + '\155', '\131', '\040', '\161', '\165', '\040', '\061', '\012', + '\161', '\132', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\114', '\142', '\160', '\040', '\160', '\162', '\040', + '\061', '\012', '\104', '\147', '\161', '\040', '\156', '\147', + '\040', '\061', '\012', '\126', '\153', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\167', '\161', '\125', '\040', + '\161', '\165', '\040', '\061', '\012', '\115', '\161', '\153', + '\040', '\161', '\165', '\040', '\061', '\012', '\167', '\125', + '\166', '\040', '\166', '\141', '\040', '\061', '\012', '\161', + '\147', '\103', '\040', '\156', '\147', '\040', '\061', '\012', + '\163', '\142', '\104', '\040', '\163', '\172', '\040', '\061', + '\012', '\123', '\161', '\171', '\040', '\161', '\165', '\040', + '\061', '\012', '\142', '\115', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\102', '\172', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\163', '\111', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\143', '\126', '\152', + '\040', '\143', '\150', '\040', '\061', '\012', '\167', '\112', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\130', + '\152', '\155', '\040', '\151', '\152', '\040', '\061', '\012', + '\110', '\155', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\141', '\121', '\144', '\040', '\141', '\156', '\040', + '\061', '\012', '\151', '\110', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\146', '\115', '\155', '\040', '\155', + '\145', '\040', '\061', '\012', '\167', '\127', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\146', '\165', '\105', + '\040', '\161', '\165', '\040', '\061', '\012', '\155', '\103', + '\146', '\040', '\155', '\145', '\040', '\061', '\012', '\161', + '\156', '\120', '\040', '\141', '\156', '\040', '\061', '\012', + '\172', '\114', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\122', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\115', '\166', '\154', '\040', '\154', '\145', + '\040', '\061', '\012', '\155', '\122', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\171', '\146', '\112', '\040', + '\156', '\171', '\040', '\061', '\012', '\170', '\103', '\142', + '\040', '\142', '\145', '\040', '\061', '\012', '\163', '\121', + '\142', '\040', '\163', '\172', '\040', '\061', '\012', '\161', + '\165', '\103', '\040', '\165', '\156', '\040', '\061', '\012', + '\103', '\164', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\120', '\166', '\040', '\166', '\141', '\040', + '\061', '\012', '\172', '\152', '\111', '\040', '\163', '\172', + '\040', '\061', '\012', '\170', '\155', '\103', '\040', '\155', + '\145', '\040', '\061', '\012', '\170', '\144', '\112', '\040', + '\144', '\145', '\040', '\061', '\012', '\156', '\130', '\166', + '\040', '\141', '\156', '\040', '\061', '\012', '\166', '\163', + '\117', '\040', '\163', '\172', '\040', '\061', '\012', '\160', + '\122', '\144', '\040', '\144', '\145', '\040', '\061', '\012', + '\166', '\142', '\106', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\116', '\154', '\040', '\154', '\145', '\040', + '\061', '\012', '\153', '\110', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\162', '\167', '\115', '\040', '\145', + '\162', '\040', '\061', '\012', '\147', '\170', '\104', '\040', + '\156', '\147', '\040', '\061', '\012', '\121', '\150', '\151', + '\040', '\164', '\150', '\040', '\061', '\012', '\155', '\161', + '\102', '\040', '\161', '\165', '\040', '\061', '\012', '\160', + '\156', '\114', '\040', '\141', '\156', '\040', '\061', '\012', + '\142', '\113', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\151', '\161', '\116', '\040', '\151', '\156', '\040', + '\061', '\012', '\144', '\153', '\130', '\040', '\144', '\145', + '\040', '\061', '\012', '\142', '\121', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\142', '\116', '\152', '\040', + '\151', '\152', '\040', '\061', '\012', '\124', '\154', '\153', + '\040', '\154', '\145', '\040', '\061', '\012', '\116', '\154', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\103', + '\170', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\115', '\161', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\120', '\166', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\172', '\167', '\132', '\040', '\163', '\172', + '\040', '\061', '\012', '\160', '\107', '\142', '\040', '\160', + '\162', '\040', '\061', '\012', '\156', '\162', '\106', '\040', + '\141', '\156', '\040', '\061', '\012', '\142', '\153', '\123', + '\040', '\153', '\141', '\040', '\061', '\012', '\144', '\122', + '\166', '\040', '\144', '\145', '\040', '\061', '\012', '\152', + '\112', '\155', '\040', '\151', '\152', '\040', '\061', '\012', + '\151', '\161', '\106', '\040', '\151', '\156', '\040', '\061', + '\012', '\146', '\107', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\156', '\170', '\127', '\040', '\141', '\156', + '\040', '\061', '\012', '\170', '\163', '\127', '\040', '\163', + '\172', '\040', '\061', '\012', '\155', '\146', '\121', '\040', + '\155', '\145', '\040', '\061', '\012', '\146', '\147', '\120', + '\040', '\156', '\147', '\040', '\061', '\012', '\152', '\154', + '\110', '\040', '\154', '\145', '\040', '\061', '\012', '\156', + '\162', '\111', '\040', '\141', '\156', '\040', '\061', '\012', + '\153', '\130', '\166', '\040', '\153', '\141', '\040', '\061', + '\012', '\126', '\160', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\172', '\115', '\153', '\040', '\163', '\172', + '\040', '\061', '\012', '\160', '\110', '\146', '\040', '\160', + '\162', '\040', '\061', '\012', '\152', '\144', '\115', '\040', + '\144', '\145', '\040', '\061', '\012', '\142', '\161', '\112', + '\040', '\161', '\165', '\040', '\061', '\012', '\103', '\153', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\172', + '\113', '\166', '\040', '\163', '\172', '\040', '\061', '\012', + '\152', '\172', '\107', '\040', '\163', '\172', '\040', '\061', + '\012', '\165', '\111', '\170', '\040', '\161', '\165', '\040', + '\061', '\012', '\171', '\116', '\155', '\040', '\155', '\145', + '\040', '\061', '\012', '\152', '\131', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\146', '\167', '\114', '\040', + '\167', '\141', '\040', '\061', '\012', '\144', '\132', '\170', + '\040', '\144', '\145', '\040', '\061', '\012', '\166', '\147', + '\106', '\040', '\156', '\147', '\040', '\061', '\012', '\167', + '\130', '\151', '\040', '\151', '\156', '\040', '\061', '\012', + '\166', '\132', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\103', '\164', '\146', '\040', '\164', '\150', '\040', + '\061', '\012', '\170', '\161', '\103', '\040', '\161', '\165', + '\040', '\061', '\012', '\161', '\117', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\171', '\147', '\130', '\040', + '\156', '\147', '\040', '\061', '\012', '\153', '\127', '\153', + '\040', '\153', '\141', '\040', '\061', '\012', '\147', '\162', + '\106', '\040', '\156', '\147', '\040', '\061', '\012', '\161', + '\156', '\130', '\040', '\141', '\156', '\040', '\061', '\012', + '\170', '\125', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\160', '\155', '\103', '\040', '\155', '\145', '\040', + '\061', '\012', '\165', '\172', '\105', '\040', '\161', '\165', + '\040', '\061', '\012', '\111', '\166', '\167', '\040', '\166', + '\141', '\040', '\061', '\012', '\147', '\166', '\111', '\040', + '\156', '\147', '\040', '\061', '\012', '\153', '\156', '\132', + '\040', '\141', '\156', '\040', '\061', '\012', '\154', '\170', + '\132', '\040', '\154', '\145', '\040', '\061', '\012', '\130', + '\167', '\146', '\040', '\167', '\141', '\040', '\061', '\012', + '\104', '\161', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\113', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\126', '\167', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\170', '\123', '\142', '\040', '\142', + '\145', '\040', '\061', '\012', '\110', '\167', '\160', '\040', + '\160', '\162', '\040', '\061', '\012', '\171', '\116', '\170', + '\040', '\156', '\171', '\040', '\061', '\012', '\171', '\157', + '\121', '\040', '\157', '\156', '\040', '\061', '\012', '\143', + '\123', '\170', '\040', '\143', '\150', '\040', '\061', '\012', + '\105', '\166', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\111', '\167', '\040', '\164', '\150', '\040', + '\061', '\012', '\144', '\146', '\132', '\040', '\144', '\145', + '\040', '\061', '\012', '\150', '\172', '\120', '\040', '\164', + '\150', '\040', '\061', '\012', '\170', '\102', '\153', '\040', + '\153', '\141', '\040', '\061', '\012', '\153', '\161', '\162', + '\040', '\161', '\165', '\040', '\061', '\012', '\171', '\102', + '\155', '\040', '\155', '\145', '\040', '\061', '\012', '\154', + '\112', '\152', '\040', '\154', '\145', '\040', '\061', '\012', + '\143', '\152', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\162', '\127', '\040', '\145', '\162', '\040', + '\061', '\012', '\161', '\141', '\104', '\040', '\141', '\156', + '\040', '\061', '\012', '\167', '\104', '\146', '\040', '\167', + '\141', '\040', '\061', '\012', '\114', '\170', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\172', '\121', '\146', + '\040', '\146', '\157', '\040', '\061', '\012', '\112', '\164', + '\161', '\040', '\164', '\150', '\040', '\061', '\012', '\161', + '\122', '\166', '\040', '\161', '\165', '\040', '\061', '\012', + '\107', '\146', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\130', '\142', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\167', '\132', '\142', '\040', '\167', '\141', + '\040', '\061', '\012', '\163', '\162', '\121', '\040', '\145', + '\162', '\040', '\061', '\012', '\147', '\112', '\161', '\040', + '\156', '\147', '\040', '\061', '\012', '\152', '\106', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\147', '\116', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\122', + '\153', '\162', '\040', '\145', '\162', '\040', '\061', '\012', + '\160', '\172', '\112', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\142', '\101', '\040', '\154', '\145', '\040', + '\061', '\012', '\143', '\102', '\161', '\040', '\143', '\150', + '\040', '\061', '\012', '\113', '\171', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\170', '\143', '\117', '\040', + '\143', '\150', '\040', '\061', '\012', '\172', '\130', '\162', + '\040', '\145', '\162', '\040', '\061', '\012', '\143', '\126', + '\163', '\040', '\143', '\150', '\040', '\061', '\012', '\162', + '\131', '\155', '\040', '\145', '\162', '\040', '\061', '\012', + '\153', '\126', '\155', '\040', '\153', '\141', '\040', '\061', + '\012', '\146', '\143', '\132', '\040', '\143', '\150', '\040', + '\061', '\012', '\146', '\172', '\103', '\040', '\163', '\172', + '\040', '\061', '\012', '\164', '\113', '\160', '\040', '\164', + '\150', '\040', '\061', '\012', '\147', '\120', '\172', '\040', + '\156', '\147', '\040', '\061', '\012', '\161', '\143', '\114', + '\040', '\143', '\150', '\040', '\061', '\012', '\131', '\152', + '\162', '\040', '\145', '\162', '\040', '\061', '\012', '\172', + '\170', '\125', '\040', '\163', '\172', '\040', '\061', '\012', + '\170', '\142', '\124', '\040', '\142', '\145', '\040', '\061', + '\012', '\156', '\166', '\130', '\040', '\141', '\156', '\040', + '\061', '\012', '\161', '\155', '\122', '\040', '\161', '\165', + '\040', '\061', '\012', '\142', '\170', '\114', '\040', '\142', + '\145', '\040', '\061', '\012', '\130', '\167', '\167', '\040', + '\167', '\141', '\040', '\061', '\012', '\152', '\123', '\146', + '\040', '\151', '\152', '\040', '\061', '\012', '\154', '\116', + '\146', '\040', '\154', '\145', '\040', '\061', '\012', '\172', + '\124', '\163', '\040', '\163', '\172', '\040', '\061', '\012', + '\153', '\106', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\114', '\172', '\040', '\161', '\165', '\040', + '\061', '\012', '\162', '\162', '\130', '\040', '\145', '\162', + '\040', '\061', '\012', '\167', '\130', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\172', '\166', '\105', '\040', + '\163', '\172', '\040', '\061', '\012', '\110', '\167', '\170', + '\040', '\167', '\141', '\040', '\061', '\012', '\161', '\106', + '\155', '\040', '\161', '\165', '\040', '\061', '\012', '\143', + '\147', '\122', '\040', '\143', '\150', '\040', '\061', '\012', + '\160', '\104', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\117', '\161', '\142', '\040', '\161', '\165', '\040', + '\061', '\012', '\163', '\126', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\130', '\164', '\170', '\040', '\164', + '\150', '\040', '\061', '\012', '\121', '\167', '\164', '\040', + '\164', '\150', '\040', '\061', '\012', '\127', '\146', '\145', + '\040', '\145', '\162', '\040', '\061', '\012', '\120', '\143', + '\170', '\040', '\143', '\150', '\040', '\061', '\012', '\142', + '\160', '\117', '\040', '\160', '\162', '\040', '\061', '\012', + '\103', '\167', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\170', '\117', '\040', '\167', '\141', '\040', + '\061', '\012', '\142', '\126', '\163', '\040', '\163', '\172', + '\040', '\061', '\012', '\152', '\106', '\167', '\040', '\151', + '\152', '\040', '\061', '\012', '\146', '\156', '\106', '\040', + '\141', '\156', '\040', '\061', '\012', '\153', '\170', '\110', + '\040', '\153', '\141', '\040', '\061', '\012', '\131', '\167', + '\163', '\040', '\163', '\172', '\040', '\061', '\012', '\147', + '\144', '\104', '\040', '\156', '\147', '\040', '\061', '\012', + '\152', '\127', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\124', '\154', '\040', '\143', '\150', '\040', + '\061', '\012', '\153', '\155', '\127', '\040', '\153', '\141', + '\040', '\061', '\012', '\155', '\150', '\127', '\040', '\164', + '\150', '\040', '\061', '\012', '\142', '\172', '\124', '\040', + '\163', '\172', '\040', '\061', '\012', '\162', '\166', '\112', + '\040', '\145', '\162', '\040', '\061', '\012', '\170', '\143', + '\112', '\040', '\143', '\150', '\040', '\061', '\012', '\166', + '\153', '\123', '\040', '\153', '\141', '\040', '\061', '\012', + '\163', '\130', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\163', '\103', '\166', '\040', '\163', '\172', '\040', + '\061', '\012', '\116', '\164', '\160', '\040', '\164', '\150', + '\040', '\061', '\012', '\157', '\110', '\150', '\040', '\154', + '\157', '\040', '\061', '\012', '\131', '\166', '\163', '\040', + '\163', '\172', '\040', '\061', '\012', '\160', '\126', '\146', + '\040', '\160', '\162', '\040', '\061', '\012', '\153', '\105', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\161', + '\146', '\105', '\040', '\161', '\165', '\040', '\061', '\012', + '\157', '\127', '\155', '\040', '\157', '\156', '\040', '\061', + '\012', '\164', '\115', '\167', '\040', '\164', '\150', '\040', + '\061', '\012', '\172', '\131', '\160', '\040', '\163', '\172', + '\040', '\061', '\012', '\156', '\106', '\167', '\040', '\141', + '\156', '\040', '\061', '\012', '\171', '\121', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\172', '\121', '\152', + '\040', '\163', '\172', '\040', '\061', '\012', '\167', '\113', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\155', + '\113', '\146', '\040', '\155', '\145', '\040', '\061', '\012', + '\165', '\114', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\111', '\142', '\040', '\167', '\141', '\040', + '\061', '\012', '\167', '\162', '\110', '\040', '\145', '\162', + '\040', '\061', '\012', '\160', '\147', '\114', '\040', '\156', + '\147', '\040', '\061', '\012', '\114', '\142', '\164', '\040', + '\164', '\150', '\040', '\061', '\012', '\172', '\152', '\106', + '\040', '\163', '\172', '\040', '\061', '\012', '\161', '\106', + '\160', '\040', '\161', '\165', '\040', '\061', '\012', '\172', + '\144', '\130', '\040', '\144', '\145', '\040', '\061', '\012', + '\167', '\124', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\112', '\167', '\154', '\040', '\154', '\145', '\040', + '\061', '\012', '\154', '\170', '\125', '\040', '\154', '\145', + '\040', '\061', '\012', '\150', '\152', '\101', '\040', '\164', + '\150', '\040', '\061', '\012', '\151', '\120', '\147', '\040', + '\151', '\156', '\040', '\061', '\012', '\130', '\156', '\163', + '\040', '\141', '\156', '\040', '\061', '\012', '\167', '\153', + '\127', '\040', '\153', '\141', '\040', '\061', '\012', '\160', + '\146', '\120', '\040', '\160', '\162', '\040', '\061', '\012', + '\104', '\171', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\127', '\165', '\040', '\161', '\165', '\040', + '\061', '\012', '\161', '\172', '\122', '\040', '\161', '\165', + '\040', '\061', '\012', '\131', '\152', '\172', '\040', '\163', + '\172', '\040', '\061', '\012', '\164', '\167', '\130', '\040', + '\164', '\150', '\040', '\061', '\012', '\116', '\167', '\152', + '\040', '\151', '\152', '\040', '\061', '\012', '\152', '\142', + '\102', '\040', '\151', '\152', '\040', '\061', '\012', '\161', + '\167', '\122', '\040', '\161', '\165', '\040', '\061', '\012', + '\131', '\164', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\154', '\130', '\040', '\154', '\145', '\040', + '\061', '\012', '\170', '\132', '\153', '\040', '\153', '\141', + '\040', '\061', '\012', '\131', '\155', '\167', '\040', '\155', + '\145', '\040', '\061', '\012', '\167', '\146', '\130', '\040', + '\167', '\141', '\040', '\061', '\012', '\126', '\161', '\171', + '\040', '\161', '\165', '\040', '\061', '\012', '\130', '\161', + '\156', '\040', '\141', '\156', '\040', '\061', '\012', '\171', + '\125', '\167', '\040', '\167', '\141', '\040', '\061', '\012', + '\152', '\172', '\124', '\040', '\152', '\157', '\040', '\061', + '\012', '\153', '\116', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\160', '\155', '\121', '\040', '\155', '\145', + '\040', '\061', '\012', '\144', '\130', '\162', '\040', '\145', + '\162', '\040', '\061', '\012', '\171', '\154', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\164', '\127', '\172', + '\040', '\164', '\150', '\040', '\061', '\012', '\113', '\166', + '\162', '\040', '\145', '\162', '\040', '\061', '\012', '\142', + '\150', '\121', '\040', '\164', '\150', '\040', '\061', '\012', + '\165', '\112', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\142', '\124', '\040', '\160', '\162', '\040', + '\061', '\012', '\141', '\102', '\146', '\040', '\141', '\156', + '\040', '\061', '\012', '\122', '\150', '\152', '\040', '\164', + '\150', '\040', '\061', '\012', '\165', '\101', '\170', '\040', + '\161', '\165', '\040', '\061', '\012', '\102', '\147', '\170', + '\040', '\156', '\147', '\040', '\061', '\012', '\152', '\161', + '\116', '\040', '\161', '\165', '\040', '\061', '\012', '\152', + '\144', '\103', '\040', '\151', '\152', '\040', '\061', '\012', + '\146', '\102', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\143', '\130', '\153', '\040', '\143', '\150', '\040', + '\061', '\012', '\156', '\155', '\115', '\040', '\141', '\156', + '\040', '\061', '\012', '\170', '\122', '\162', '\040', '\145', + '\162', '\040', '\061', '\012', '\110', '\153', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\144', '\150', '\132', + '\040', '\164', '\150', '\040', '\061', '\012', '\106', '\171', + '\160', '\040', '\160', '\162', '\040', '\061', '\012', '\153', + '\107', '\155', '\040', '\153', '\141', '\040', '\061', '\012', + '\163', '\107', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\113', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\166', '\104', '\172', '\040', '\163', '\172', + '\040', '\061', '\012', '\166', '\114', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\154', '\112', '\163', '\040', + '\154', '\145', '\040', '\061', '\012', '\172', '\116', '\156', + '\040', '\141', '\156', '\040', '\061', '\012', '\127', '\147', + '\152', '\040', '\156', '\147', '\040', '\061', '\012', '\152', + '\155', '\114', '\040', '\151', '\152', '\040', '\061', '\012', + '\147', '\126', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\106', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\172', '\142', '\104', '\040', '\163', '\172', + '\040', '\061', '\012', '\153', '\124', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\144', '\167', '\130', '\040', + '\144', '\145', '\040', '\061', '\012', '\170', '\122', '\154', + '\040', '\154', '\145', '\040', '\061', '\012', '\101', '\172', + '\166', '\040', '\163', '\172', '\040', '\061', '\012', '\142', + '\121', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\161', '\121', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\157', '\132', '\040', '\157', '\156', '\040', + '\061', '\012', '\152', '\120', '\163', '\040', '\163', '\172', + '\040', '\061', '\012', '\152', '\171', '\107', '\040', '\151', + '\152', '\040', '\061', '\012', '\153', '\130', '\152', '\040', + '\153', '\141', '\040', '\061', '\012', '\171', '\102', '\166', + '\040', '\166', '\141', '\040', '\061', '\012', '\156', '\167', + '\120', '\040', '\141', '\156', '\040', '\061', '\012', '\170', + '\156', '\101', '\040', '\141', '\156', '\040', '\061', '\012', + '\142', '\113', '\146', '\040', '\142', '\145', '\040', '\061', + '\012', '\161', '\142', '\120', '\040', '\161', '\165', '\040', + '\061', '\012', '\166', '\107', '\163', '\040', '\163', '\172', + '\040', '\061', '\012', '\152', '\152', '\107', '\040', '\151', + '\152', '\040', '\061', '\012', '\113', '\161', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\172', '\126', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\167', '\123', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\163', + '\127', '\155', '\040', '\163', '\172', '\040', '\061', '\012', + '\146', '\104', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\110', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\146', '\131', '\160', '\040', '\160', '\162', + '\040', '\061', '\012', '\172', '\162', '\127', '\040', '\145', + '\162', '\040', '\061', '\012', '\154', '\104', '\170', '\040', + '\154', '\145', '\040', '\061', '\012', '\150', '\121', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\102', '\144', + '\160', '\040', '\144', '\145', '\040', '\061', '\012', '\146', + '\161', '\132', '\040', '\161', '\165', '\040', '\061', '\012', + '\157', '\121', '\155', '\040', '\157', '\156', '\040', '\061', + '\012', '\121', '\163', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\170', '\152', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\115', '\146', '\166', '\040', '\166', + '\141', '\040', '\061', '\012', '\172', '\142', '\121', '\040', + '\163', '\172', '\040', '\061', '\012', '\161', '\165', '\122', + '\040', '\165', '\156', '\040', '\061', '\012', '\143', '\115', + '\142', '\040', '\143', '\150', '\040', '\061', '\012', '\172', + '\161', '\104', '\040', '\161', '\165', '\040', '\061', '\012', + '\144', '\130', '\146', '\040', '\144', '\145', '\040', '\061', + '\012', '\162', '\110', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\152', '\150', '\106', '\040', '\164', '\150', + '\040', '\061', '\012', '\156', '\116', '\146', '\040', '\141', + '\156', '\040', '\061', '\012', '\167', '\110', '\142', '\040', + '\167', '\141', '\040', '\061', '\012', '\124', '\160', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\142', '\152', + '\131', '\040', '\151', '\152', '\040', '\061', '\012', '\143', + '\112', '\161', '\040', '\143', '\150', '\040', '\061', '\012', + '\154', '\103', '\153', '\040', '\154', '\145', '\040', '\061', + '\012', '\120', '\146', '\160', '\040', '\160', '\162', '\040', + '\061', '\012', '\117', '\161', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\146', '\155', '\122', '\040', '\155', + '\145', '\040', '\061', '\012', '\121', '\160', '\165', '\040', + '\161', '\165', '\040', '\061', '\012', '\116', '\143', '\166', + '\040', '\143', '\150', '\040', '\061', '\012', '\161', '\131', + '\162', '\040', '\161', '\165', '\040', '\061', '\012', '\163', + '\146', '\101', '\040', '\163', '\172', '\040', '\061', '\012', + '\146', '\162', '\123', '\040', '\145', '\162', '\040', '\061', + '\012', '\107', '\160', '\146', '\040', '\160', '\162', '\040', + '\061', '\012', '\152', '\155', '\104', '\040', '\151', '\152', + '\040', '\061', '\012', '\150', '\167', '\111', '\040', '\164', + '\150', '\040', '\061', '\012', '\122', '\142', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\152', '\150', '\102', + '\040', '\164', '\150', '\040', '\061', '\012', '\170', '\130', + '\152', '\040', '\151', '\152', '\040', '\061', '\012', '\161', + '\131', '\144', '\040', '\161', '\165', '\040', '\061', '\012', + '\163', '\126', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\143', '\103', '\172', '\040', '\143', '\150', '\040', + '\061', '\012', '\161', '\115', '\154', '\040', '\161', '\165', + '\040', '\061', '\012', '\146', '\160', '\113', '\040', '\160', + '\162', '\040', '\061', '\012', '\150', '\126', '\171', '\040', + '\164', '\150', '\040', '\061', '\012', '\154', '\143', '\112', + '\040', '\143', '\150', '\040', '\061', '\012', '\117', '\153', + '\152', '\040', '\151', '\152', '\040', '\061', '\012', '\161', + '\112', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\152', '\114', '\160', '\040', '\151', '\152', '\040', '\061', + '\012', '\156', '\131', '\146', '\040', '\141', '\156', '\040', + '\061', '\012', '\156', '\160', '\106', '\040', '\157', '\156', + '\040', '\061', '\012', '\162', '\127', '\153', '\040', '\145', + '\162', '\040', '\061', '\012', '\155', '\143', '\120', '\040', + '\143', '\150', '\040', '\061', '\012', '\156', '\132', '\155', + '\040', '\141', '\156', '\040', '\061', '\012', '\146', '\131', + '\142', '\040', '\146', '\157', '\040', '\061', '\012', '\172', + '\142', '\103', '\040', '\163', '\172', '\040', '\061', '\012', + '\156', '\102', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\152', '\171', '\040', '\151', '\152', '\040', + '\061', '\012', '\142', '\111', '\170', '\040', '\142', '\145', + '\040', '\061', '\012', '\164', '\167', '\116', '\040', '\164', + '\150', '\040', '\061', '\012', '\107', '\147', '\153', '\040', + '\156', '\147', '\040', '\061', '\012', '\103', '\172', '\155', + '\040', '\163', '\172', '\040', '\061', '\012', '\152', '\164', + '\117', '\040', '\164', '\150', '\040', '\061', '\012', '\156', + '\122', '\154', '\040', '\141', '\156', '\040', '\061', '\012', + '\152', '\171', '\103', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\105', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\166', '\155', '\110', '\040', '\166', '\141', + '\040', '\061', '\012', '\167', '\164', '\121', '\040', '\164', + '\150', '\040', '\061', '\012', '\167', '\111', '\146', '\040', + '\167', '\141', '\040', '\061', '\012', '\152', '\111', '\146', + '\040', '\151', '\152', '\040', '\061', '\012', '\161', '\142', + '\115', '\040', '\161', '\165', '\040', '\061', '\012', '\122', + '\167', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\146', '\161', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\146', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\152', '\146', '\127', '\040', '\151', '\152', + '\040', '\061', '\012', '\167', '\127', '\155', '\040', '\155', + '\145', '\040', '\061', '\012', '\127', '\160', '\160', '\040', + '\160', '\162', '\040', '\061', '\012', '\115', '\147', '\152', + '\040', '\156', '\147', '\040', '\061', '\012', '\144', '\123', + '\146', '\040', '\144', '\145', '\040', '\061', '\012', '\167', + '\131', '\166', '\040', '\166', '\141', '\040', '\061', '\012', + '\143', '\143', '\111', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\154', '\124', '\040', '\154', '\145', '\040', + '\061', '\012', '\107', '\161', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\103', '\155', '\172', '\040', '\163', + '\172', '\040', '\061', '\012', '\110', '\146', '\153', '\040', + '\153', '\141', '\040', '\061', '\012', '\161', '\102', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\171', '\103', + '\146', '\040', '\156', '\171', '\040', '\061', '\012', '\161', + '\172', '\117', '\040', '\161', '\165', '\040', '\061', '\012', + '\171', '\144', '\106', '\040', '\144', '\145', '\040', '\061', + '\012', '\126', '\144', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\160', '\112', '\144', '\040', '\144', '\145', + '\040', '\061', '\012', '\163', '\146', '\122', '\040', '\163', + '\172', '\040', '\061', '\012', '\144', '\154', '\126', '\040', + '\154', '\145', '\040', '\061', '\012', '\152', '\117', '\144', + '\040', '\144', '\145', '\040', '\061', '\012', '\156', '\146', + '\106', '\040', '\141', '\156', '\040', '\061', '\012', '\167', + '\124', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\162', '\107', '\153', '\040', '\145', '\162', '\040', '\061', + '\012', '\170', '\101', '\167', '\040', '\167', '\141', '\040', + '\061', '\012', '\166', '\146', '\106', '\040', '\166', '\141', + '\040', '\061', '\012', '\104', '\172', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\153', '\106', '\160', '\040', + '\153', '\141', '\040', '\061', '\012', '\152', '\124', '\155', + '\040', '\151', '\152', '\040', '\061', '\012', '\156', '\116', + '\161', '\040', '\141', '\156', '\040', '\061', '\012', '\161', + '\143', '\116', '\040', '\143', '\150', '\040', '\061', '\012', + '\112', '\152', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\164', '\113', '\146', '\040', '\164', '\150', '\040', + '\061', '\012', '\132', '\162', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\150', '\155', '\113', '\040', '\164', + '\150', '\040', '\061', '\012', '\115', '\161', '\172', '\040', + '\161', '\165', '\040', '\061', '\012', '\170', '\146', '\122', + '\040', '\146', '\157', '\040', '\061', '\012', '\167', '\121', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\155', + '\161', '\107', '\040', '\161', '\165', '\040', '\061', '\012', + '\170', '\125', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\157', '\151', '\125', '\040', '\151', '\156', '\040', + '\061', '\012', '\161', '\163', '\123', '\040', '\161', '\165', + '\040', '\061', '\012', '\161', '\107', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\161', '\164', '\117', '\040', + '\164', '\150', '\040', '\061', '\012', '\164', '\120', '\142', + '\040', '\164', '\150', '\040', '\061', '\012', '\122', '\161', + '\155', '\040', '\161', '\165', '\040', '\061', '\012', '\166', + '\153', '\130', '\040', '\153', '\141', '\040', '\061', '\012', + '\127', '\163', '\142', '\040', '\163', '\164', '\040', '\061', + '\012', '\143', '\170', '\122', '\040', '\143', '\150', '\040', + '\061', '\012', '\146', '\132', '\162', '\040', '\145', '\162', + '\040', '\061', '\012', '\171', '\121', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\172', '\151', '\125', '\040', + '\151', '\156', '\040', '\061', '\012', '\170', '\166', '\127', + '\040', '\166', '\141', '\040', '\061', '\012', '\141', '\104', + '\170', '\040', '\141', '\156', '\040', '\061', '\012', '\142', + '\121', '\152', '\040', '\151', '\152', '\040', '\061', '\012', + '\152', '\170', '\103', '\040', '\151', '\152', '\040', '\061', + '\012', '\124', '\167', '\153', '\040', '\153', '\141', '\040', + '\061', '\012', '\163', '\121', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\102', '\146', '\170', '\040', '\146', + '\157', '\040', '\061', '\012', '\141', '\107', '\152', '\040', + '\141', '\156', '\040', '\061', '\012', '\120', '\147', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\110', '\172', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\161', + '\147', '\127', '\040', '\156', '\147', '\040', '\061', '\012', + '\153', '\144', '\106', '\040', '\144', '\145', '\040', '\061', + '\012', '\153', '\142', '\131', '\040', '\153', '\141', '\040', + '\061', '\012', '\121', '\152', '\170', '\040', '\151', '\152', + '\040', '\061', '\012', '\110', '\170', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\164', '\126', '\170', '\040', + '\164', '\150', '\040', '\061', '\012', '\156', '\170', '\132', + '\040', '\141', '\156', '\040', '\061', '\012', '\157', '\126', + '\144', '\040', '\157', '\156', '\040', '\061', '\012', '\110', + '\154', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\152', '\113', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\101', '\151', '\040', '\151', '\156', '\040', + '\061', '\012', '\144', '\116', '\154', '\040', '\154', '\145', + '\040', '\061', '\012', '\160', '\161', '\101', '\040', '\161', + '\165', '\040', '\061', '\012', '\145', '\111', '\166', '\040', + '\145', '\162', '\040', '\061', '\012', '\170', '\155', '\127', + '\040', '\155', '\145', '\040', '\061', '\012', '\171', '\143', + '\113', '\040', '\143', '\150', '\040', '\061', '\012', '\155', + '\121', '\144', '\040', '\144', '\145', '\040', '\061', '\012', + '\150', '\155', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\154', '\106', '\040', '\141', '\156', '\040', + '\061', '\012', '\107', '\153', '\154', '\040', '\154', '\145', + '\040', '\061', '\012', '\161', '\102', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\162', '\150', '\121', '\040', + '\164', '\150', '\040', '\061', '\012', '\132', '\156', '\153', + '\040', '\141', '\156', '\040', '\061', '\012', '\126', '\146', + '\160', '\040', '\160', '\162', '\040', '\061', '\012', '\156', + '\102', '\156', '\040', '\141', '\156', '\040', '\061', '\012', + '\161', '\166', '\114', '\040', '\161', '\165', '\040', '\061', + '\012', '\141', '\161', '\116', '\040', '\141', '\156', '\040', + '\061', '\012', '\153', '\114', '\146', '\040', '\153', '\141', + '\040', '\061', '\012', '\172', '\112', '\162', '\040', '\145', + '\162', '\040', '\061', '\012', '\164', '\121', '\167', '\040', + '\164', '\150', '\040', '\061', '\012', '\163', '\127', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\142', '\167', + '\127', '\040', '\167', '\141', '\040', '\061', '\012', '\166', + '\172', '\102', '\040', '\163', '\172', '\040', '\061', '\012', + '\171', '\171', '\122', '\040', '\156', '\171', '\040', '\061', + '\012', '\161', '\161', '\116', '\040', '\161', '\165', '\040', + '\061', '\012', '\167', '\171', '\111', '\040', '\156', '\171', + '\040', '\061', '\012', '\152', '\172', '\112', '\040', '\163', + '\172', '\040', '\061', '\012', '\161', '\147', '\111', '\040', + '\161', '\165', '\040', '\061', '\012', '\142', '\147', '\121', + '\040', '\156', '\147', '\040', '\061', '\012', '\171', '\114', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\126', + '\161', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\130', '\156', '\162', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\110', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\141', '\121', '\147', '\040', '\141', '\156', + '\040', '\061', '\012', '\143', '\106', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\172', '\152', '\121', '\040', + '\163', '\172', '\040', '\061', '\012', '\147', '\160', '\104', + '\040', '\156', '\147', '\040', '\061', '\012', '\170', '\172', + '\116', '\040', '\163', '\172', '\040', '\061', '\012', '\151', + '\111', '\167', '\040', '\151', '\156', '\040', '\061', '\012', + '\144', '\121', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\121', '\171', '\040', '\160', '\162', '\040', + '\061', '\012', '\130', '\171', '\170', '\040', '\156', '\171', + '\040', '\061', '\012', '\163', '\127', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\152', '\106', '\144', '\040', + '\144', '\145', '\040', '\061', '\012', '\142', '\160', '\106', + '\040', '\160', '\162', '\040', '\061', '\012', '\126', '\163', + '\166', '\040', '\163', '\164', '\040', '\061', '\012', '\121', + '\161', '\154', '\040', '\161', '\165', '\040', '\061', '\012', + '\167', '\172', '\124', '\040', '\163', '\172', '\040', '\061', + '\012', '\163', '\161', '\121', '\040', '\161', '\165', '\040', + '\061', '\012', '\113', '\172', '\155', '\040', '\163', '\172', + '\040', '\061', '\012', '\157', '\106', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\147', '\153', '\112', '\040', + '\156', '\147', '\040', '\061', '\012', '\150', '\153', '\110', + '\040', '\164', '\150', '\040', '\061', '\012', '\161', '\114', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\142', + '\155', '\125', '\040', '\155', '\145', '\040', '\061', '\012', + '\143', '\162', '\112', '\040', '\143', '\150', '\040', '\061', + '\012', '\163', '\154', '\130', '\040', '\154', '\145', '\040', + '\061', '\012', '\124', '\172', '\170', '\040', '\163', '\172', + '\040', '\061', '\012', '\161', '\142', '\170', '\040', '\161', + '\165', '\040', '\061', '\012', '\153', '\160', '\111', '\040', + '\153', '\141', '\040', '\061', '\012', '\170', '\103', '\146', + '\040', '\146', '\157', '\040', '\061', '\012', '\106', '\155', + '\154', '\040', '\154', '\145', '\040', '\061', '\012', '\121', + '\150', '\152', '\040', '\164', '\150', '\040', '\061', '\012', + '\164', '\121', '\163', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\122', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\131', '\143', '\142', '\040', '\143', '\150', + '\040', '\061', '\012', '\143', '\152', '\120', '\040', '\143', + '\150', '\040', '\061', '\012', '\171', '\165', '\105', '\040', + '\161', '\165', '\040', '\061', '\012', '\147', '\111', '\151', + '\040', '\151', '\156', '\040', '\061', '\012', '\153', '\127', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\112', + '\167', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\146', '\126', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\152', '\161', '\171', '\040', '\161', '\165', '\040', + '\061', '\012', '\127', '\172', '\160', '\040', '\163', '\172', + '\040', '\061', '\012', '\103', '\167', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\161', '\105', '\171', '\040', + '\161', '\165', '\040', '\061', '\012', '\152', '\162', '\130', + '\040', '\145', '\162', '\040', '\061', '\012', '\113', '\161', + '\151', '\040', '\151', '\156', '\040', '\061', '\012', '\154', + '\131', '\166', '\040', '\154', '\145', '\040', '\061', '\012', + '\144', '\107', '\166', '\040', '\144', '\145', '\040', '\061', + '\012', '\103', '\167', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\156', '\104', '\166', '\040', '\141', '\156', + '\040', '\061', '\012', '\117', '\152', '\155', '\040', '\151', + '\152', '\040', '\061', '\012', '\104', '\156', '\170', '\040', + '\141', '\156', '\040', '\061', '\012', '\166', '\162', '\106', + '\040', '\145', '\162', '\040', '\061', '\012', '\112', '\155', + '\162', '\040', '\145', '\162', '\040', '\061', '\012', '\172', + '\146', '\111', '\040', '\163', '\172', '\040', '\061', '\012', + '\142', '\161', '\124', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\166', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\156', '\120', '\160', '\040', '\141', '\156', + '\040', '\061', '\012', '\141', '\126', '\167', '\040', '\141', + '\156', '\040', '\061', '\012', '\167', '\102', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\153', '\126', '\142', + '\040', '\153', '\141', '\040', '\061', '\012', '\147', '\143', + '\110', '\040', '\143', '\150', '\040', '\061', '\012', '\130', + '\142', '\163', '\040', '\163', '\172', '\040', '\061', '\012', + '\164', '\122', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\121', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\110', '\170', '\145', '\040', '\145', '\162', + '\040', '\061', '\012', '\104', '\156', '\167', '\040', '\141', + '\156', '\040', '\061', '\012', '\170', '\127', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\160', '\107', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\150', '\147', + '\111', '\040', '\164', '\150', '\040', '\061', '\012', '\171', + '\167', '\120', '\040', '\167', '\141', '\040', '\061', '\012', + '\156', '\162', '\127', '\040', '\141', '\156', '\040', '\061', + '\012', '\151', '\126', '\161', '\040', '\144', '\151', '\040', + '\061', '\012', '\170', '\172', '\105', '\040', '\163', '\172', + '\040', '\061', '\012', '\126', '\170', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\114', '\172', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\112', '\167', '\160', + '\040', '\160', '\162', '\040', '\061', '\012', '\147', '\103', + '\161', '\040', '\156', '\147', '\040', '\061', '\012', '\117', + '\164', '\161', '\040', '\164', '\150', '\040', '\061', '\012', + '\167', '\166', '\120', '\040', '\166', '\141', '\040', '\061', + '\012', '\143', '\116', '\162', '\040', '\143', '\150', '\040', + '\061', '\012', '\151', '\130', '\161', '\040', '\151', '\156', + '\040', '\061', '\012', '\121', '\156', '\154', '\040', '\151', + '\156', '\040', '\061', '\012', '\164', '\120', '\172', '\040', + '\164', '\150', '\040', '\061', '\012', '\150', '\111', '\142', + '\040', '\164', '\150', '\040', '\061', '\012', '\141', '\120', + '\147', '\040', '\141', '\156', '\040', '\061', '\012', '\172', + '\166', '\167', '\040', '\163', '\172', '\040', '\061', '\012', + '\156', '\161', '\117', '\040', '\141', '\156', '\040', '\061', + '\012', '\163', '\161', '\117', '\040', '\161', '\165', '\040', + '\061', '\012', '\142', '\152', '\121', '\040', '\151', '\152', + '\040', '\061', '\012', '\154', '\167', '\121', '\040', '\154', + '\145', '\040', '\061', '\012', '\160', '\105', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\142', '\127', '\152', + '\040', '\151', '\152', '\040', '\061', '\012', '\163', '\167', + '\124', '\040', '\163', '\172', '\040', '\061', '\012', '\147', + '\155', '\131', '\040', '\156', '\147', '\040', '\061', '\012', + '\147', '\122', '\153', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\132', '\162', '\040', '\145', '\162', '\040', + '\061', '\012', '\146', '\115', '\162', '\040', '\145', '\162', + '\040', '\061', '\012', '\154', '\170', '\117', '\040', '\154', + '\145', '\040', '\061', '\012', '\153', '\142', '\121', '\040', + '\153', '\141', '\040', '\061', '\012', '\171', '\146', '\116', + '\040', '\156', '\171', '\040', '\061', '\012', '\171', '\155', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\152', + '\160', '\113', '\040', '\151', '\152', '\040', '\061', '\012', + '\127', '\152', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\155', '\127', '\040', '\155', '\145', '\040', + '\061', '\012', '\162', '\113', '\170', '\040', '\145', '\162', + '\040', '\061', '\012', '\144', '\154', '\110', '\040', '\154', + '\145', '\040', '\061', '\012', '\153', '\143', '\113', '\040', + '\143', '\150', '\040', '\061', '\012', '\166', '\142', '\126', + '\040', '\166', '\141', '\040', '\061', '\012', '\161', '\116', + '\154', '\040', '\161', '\165', '\040', '\061', '\012', '\160', + '\110', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\150', '\154', '\124', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\102', '\166', '\040', '\154', '\145', '\040', + '\061', '\012', '\157', '\141', '\106', '\040', '\141', '\156', + '\040', '\061', '\012', '\170', '\146', '\115', '\040', '\146', + '\157', '\040', '\061', '\012', '\162', '\132', '\144', '\040', + '\145', '\162', '\040', '\061', '\012', '\152', '\147', '\127', + '\040', '\156', '\147', '\040', '\061', '\012', '\110', '\166', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\106', + '\153', '\146', '\040', '\153', '\141', '\040', '\061', '\012', + '\143', '\104', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\114', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\161', '\121', '\160', '\040', '\161', '\165', + '\040', '\061', '\012', '\172', '\150', '\112', '\040', '\164', + '\150', '\040', '\061', '\012', '\151', '\166', '\121', '\040', + '\151', '\156', '\040', '\061', '\012', '\125', '\153', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\142', '\160', + '\126', '\040', '\160', '\162', '\040', '\061', '\012', '\142', + '\112', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\141', '\120', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\163', '\144', '\113', '\040', '\144', '\145', '\040', + '\061', '\012', '\143', '\107', '\146', '\040', '\143', '\150', + '\040', '\061', '\012', '\114', '\152', '\167', '\040', '\151', + '\152', '\040', '\061', '\012', '\161', '\150', '\120', '\040', + '\164', '\150', '\040', '\061', '\012', '\155', '\106', '\167', + '\040', '\155', '\145', '\040', '\061', '\012', '\146', '\111', + '\165', '\040', '\161', '\165', '\040', '\061', '\012', '\172', + '\150', '\102', '\040', '\164', '\150', '\040', '\061', '\012', + '\146', '\165', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\106', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\127', '\147', '\153', '\040', '\156', '\147', + '\040', '\061', '\012', '\106', '\161', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\172', '\155', '\146', '\040', + '\163', '\172', '\040', '\061', '\012', '\132', '\160', '\146', + '\040', '\160', '\162', '\040', '\061', '\012', '\156', '\106', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\171', + '\102', '\167', '\040', '\167', '\141', '\040', '\061', '\012', + '\147', '\111', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\102', '\146', '\040', '\146', '\157', '\040', + '\061', '\012', '\125', '\167', '\154', '\040', '\154', '\145', + '\040', '\061', '\012', '\172', '\162', '\115', '\040', '\145', + '\162', '\040', '\061', '\012', '\171', '\102', '\144', '\040', + '\144', '\145', '\040', '\061', '\012', '\122', '\154', '\146', + '\040', '\154', '\145', '\040', '\061', '\012', '\120', '\172', + '\150', '\040', '\143', '\150', '\040', '\061', '\012', '\162', + '\132', '\170', '\040', '\145', '\162', '\040', '\061', '\012', + '\161', '\126', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\170', '\112', '\040', '\144', '\145', '\040', + '\061', '\012', '\114', '\143', '\172', '\040', '\143', '\150', + '\040', '\061', '\012', '\147', '\106', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\166', '\111', '\155', '\040', + '\166', '\141', '\040', '\061', '\012', '\161', '\164', '\107', + '\040', '\164', '\150', '\040', '\061', '\012', '\161', '\142', + '\107', '\040', '\161', '\165', '\040', '\061', '\012', '\142', + '\110', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\170', '\162', '\131', '\040', '\145', '\162', '\040', '\061', + '\012', '\164', '\102', '\144', '\040', '\164', '\150', '\040', + '\061', '\012', '\156', '\113', '\161', '\040', '\141', '\156', + '\040', '\061', '\012', '\116', '\153', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\152', '\103', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\142', '\171', '\130', + '\040', '\142', '\145', '\040', '\061', '\012', '\157', '\102', + '\160', '\040', '\157', '\156', '\040', '\061', '\012', '\127', + '\152', '\172', '\040', '\163', '\172', '\040', '\061', '\012', + '\172', '\146', '\120', '\040', '\163', '\172', '\040', '\061', + '\012', '\141', '\121', '\172', '\040', '\141', '\156', '\040', + '\061', '\012', '\163', '\152', '\170', '\040', '\151', '\152', + '\040', '\061', '\012', '\156', '\146', '\127', '\040', '\141', + '\156', '\040', '\061', '\012', '\156', '\130', '\167', '\040', + '\141', '\156', '\040', '\061', '\012', '\142', '\112', '\167', + '\040', '\167', '\141', '\040', '\061', '\012', '\141', '\123', + '\146', '\040', '\141', '\156', '\040', '\061', '\012', '\151', + '\122', '\146', '\040', '\151', '\156', '\040', '\061', '\012', + '\171', '\115', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\102', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\166', '\170', '\122', '\040', '\166', '\141', + '\040', '\061', '\012', '\114', '\154', '\170', '\040', '\154', + '\145', '\040', '\061', '\012', '\171', '\107', '\163', '\040', + '\163', '\172', '\040', '\061', '\012', '\112', '\163', '\171', + '\040', '\163', '\172', '\040', '\061', '\012', '\114', '\166', + '\170', '\040', '\166', '\141', '\040', '\061', '\012', '\145', + '\106', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\167', '\142', '\115', '\040', '\167', '\141', '\040', '\061', + '\012', '\165', '\117', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\167', '\127', '\154', '\040', '\154', '\145', + '\040', '\061', '\012', '\142', '\166', '\125', '\040', '\166', + '\141', '\040', '\061', '\012', '\146', '\156', '\117', '\040', + '\141', '\156', '\040', '\061', '\012', '\155', '\172', '\111', + '\040', '\163', '\172', '\040', '\061', '\012', '\126', '\143', + '\146', '\040', '\143', '\150', '\040', '\061', '\012', '\155', + '\150', '\105', '\040', '\164', '\150', '\040', '\061', '\012', + '\166', '\147', '\121', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\147', '\120', '\040', '\156', '\147', '\040', + '\061', '\012', '\161', '\142', '\152', '\040', '\161', '\165', + '\040', '\061', '\012', '\142', '\132', '\146', '\040', '\142', + '\145', '\040', '\061', '\012', '\130', '\164', '\152', '\040', + '\164', '\150', '\040', '\061', '\012', '\171', '\131', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\152', '\144', + '\113', '\040', '\144', '\145', '\040', '\061', '\012', '\152', + '\172', '\102', '\040', '\163', '\172', '\040', '\061', '\012', + '\131', '\171', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\125', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\171', '\102', '\142', '\040', '\142', '\145', + '\040', '\061', '\012', '\161', '\152', '\115', '\040', '\161', + '\165', '\040', '\061', '\012', '\163', '\130', '\167', '\040', + '\163', '\172', '\040', '\061', '\012', '\130', '\161', '\167', + '\040', '\161', '\165', '\040', '\061', '\012', '\143', '\124', + '\142', '\040', '\143', '\150', '\040', '\061', '\012', '\152', + '\162', '\105', '\040', '\145', '\162', '\040', '\061', '\012', + '\163', '\116', '\160', '\040', '\163', '\172', '\040', '\061', + '\012', '\132', '\150', '\155', '\040', '\164', '\150', '\040', + '\061', '\012', '\170', '\126', '\163', '\040', '\163', '\172', + '\040', '\061', '\012', '\152', '\107', '\172', '\040', '\163', + '\172', '\040', '\061', '\012', '\112', '\161', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\172', '\124', '\155', + '\040', '\163', '\172', '\040', '\061', '\012', '\166', '\150', + '\105', '\040', '\164', '\150', '\040', '\061', '\012', '\144', + '\121', '\151', '\040', '\151', '\156', '\040', '\061', '\012', + '\124', '\155', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\170', '\104', '\040', '\161', '\165', '\040', + '\061', '\012', '\146', '\172', '\105', '\040', '\163', '\172', + '\040', '\061', '\012', '\166', '\115', '\162', '\040', '\145', + '\162', '\040', '\061', '\012', '\103', '\161', '\170', '\040', + '\161', '\165', '\040', '\061', '\012', '\164', '\167', '\131', + '\040', '\164', '\150', '\040', '\061', '\012', '\156', '\126', + '\172', '\040', '\141', '\156', '\040', '\061', '\012', '\154', + '\122', '\153', '\040', '\154', '\145', '\040', '\061', '\012', + '\117', '\167', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\131', '\152', '\040', '\161', '\165', '\040', + '\061', '\012', '\171', '\121', '\153', '\040', '\153', '\141', + '\040', '\061', '\012', '\116', '\154', '\146', '\040', '\154', + '\145', '\040', '\061', '\012', '\161', '\104', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\142', '\110', '\167', + '\040', '\167', '\141', '\040', '\061', '\012', '\143', '\152', + '\101', '\040', '\143', '\150', '\040', '\061', '\012', '\163', + '\147', '\125', '\040', '\156', '\147', '\040', '\061', '\012', + '\153', '\121', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\171', '\116', '\146', '\040', '\156', '\171', '\040', + '\061', '\012', '\154', '\167', '\132', '\040', '\154', '\145', + '\040', '\061', '\012', '\166', '\107', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\126', '\155', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\164', '\160', '\102', + '\040', '\164', '\150', '\040', '\061', '\012', '\143', '\106', + '\144', '\040', '\143', '\150', '\040', '\061', '\012', '\170', + '\110', '\155', '\040', '\155', '\145', '\040', '\061', '\012', + '\142', '\123', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\105', '\161', '\040', '\164', '\150', '\040', + '\061', '\012', '\145', '\167', '\121', '\040', '\145', '\162', + '\040', '\061', '\012', '\145', '\127', '\144', '\040', '\145', + '\162', '\040', '\061', '\012', '\152', '\146', '\122', '\040', + '\151', '\152', '\040', '\061', '\012', '\172', '\160', '\131', + '\040', '\163', '\172', '\040', '\061', '\012', '\143', '\166', + '\121', '\040', '\143', '\150', '\040', '\061', '\012', '\150', + '\130', '\162', '\040', '\164', '\150', '\040', '\061', '\012', + '\143', '\112', '\167', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\105', '\160', '\040', '\160', '\162', '\040', + '\061', '\012', '\116', '\170', '\154', '\040', '\154', '\145', + '\040', '\061', '\012', '\161', '\115', '\146', '\040', '\161', + '\165', '\040', '\061', '\012', '\166', '\107', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\160', '\171', '\121', + '\040', '\160', '\162', '\040', '\061', '\012', '\152', '\160', + '\125', '\040', '\151', '\152', '\040', '\061', '\012', '\170', + '\157', '\101', '\040', '\157', '\156', '\040', '\061', '\012', + '\147', '\130', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\161', '\107', '\040', '\161', '\165', '\040', + '\061', '\012', '\160', '\130', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\166', '\154', '\120', '\040', '\154', + '\145', '\040', '\061', '\012', '\114', '\172', '\166', '\040', + '\163', '\172', '\040', '\061', '\012', '\152', '\170', '\102', + '\040', '\151', '\152', '\040', '\061', '\012', '\143', '\112', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\152', + '\143', '\124', '\040', '\143', '\150', '\040', '\061', '\012', + '\127', '\164', '\155', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\114', '\147', '\040', '\143', '\150', '\040', + '\061', '\012', '\153', '\125', '\170', '\040', '\153', '\141', + '\040', '\061', '\012', '\156', '\106', '\160', '\040', '\141', + '\156', '\040', '\061', '\012', '\112', '\163', '\167', '\040', + '\163', '\172', '\040', '\061', '\012', '\163', '\102', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\152', '\106', + '\156', '\040', '\141', '\156', '\040', '\061', '\012', '\147', + '\166', '\103', '\040', '\156', '\147', '\040', '\061', '\012', + '\146', '\106', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\161', '\156', '\101', '\040', '\141', '\156', '\040', + '\061', '\012', '\132', '\142', '\142', '\040', '\142', '\145', + '\040', '\061', '\012', '\120', '\172', '\170', '\040', '\163', + '\172', '\040', '\061', '\012', '\160', '\163', '\112', '\040', + '\163', '\172', '\040', '\061', '\012', '\154', '\132', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\171', '\146', + '\120', '\040', '\156', '\171', '\040', '\061', '\012', '\147', + '\131', '\166', '\040', '\156', '\147', '\040', '\061', '\012', + '\142', '\146', '\103', '\040', '\142', '\145', '\040', '\061', + '\012', '\144', '\115', '\170', '\040', '\144', '\145', '\040', + '\061', '\012', '\150', '\154', '\116', '\040', '\164', '\150', + '\040', '\061', '\012', '\167', '\122', '\154', '\040', '\154', + '\145', '\040', '\061', '\012', '\161', '\152', '\110', '\040', + '\161', '\165', '\040', '\061', '\012', '\127', '\152', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\165', '\121', + '\160', '\040', '\161', '\165', '\040', '\061', '\012', '\172', + '\124', '\142', '\040', '\163', '\172', '\040', '\061', '\012', + '\161', '\125', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\161', '\160', '\040', '\161', '\165', '\040', + '\061', '\012', '\166', '\154', '\122', '\040', '\154', '\145', + '\040', '\061', '\012', '\152', '\161', '\130', '\040', '\161', + '\165', '\040', '\061', '\012', '\163', '\167', '\122', '\040', + '\163', '\172', '\040', '\061', '\012', '\161', '\115', '\171', + '\040', '\156', '\171', '\040', '\061', '\012', '\172', '\153', + '\124', '\040', '\163', '\172', '\040', '\061', '\012', '\171', + '\161', '\130', '\040', '\161', '\165', '\040', '\061', '\012', + '\156', '\154', '\122', '\040', '\141', '\156', '\040', '\061', + '\012', '\110', '\161', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\141', '\141', '\112', '\040', '\141', '\156', + '\040', '\061', '\012', '\154', '\113', '\167', '\040', '\154', + '\145', '\040', '\061', '\012', '\142', '\172', '\102', '\040', + '\163', '\172', '\040', '\061', '\012', '\126', '\147', '\153', + '\040', '\156', '\147', '\040', '\061', '\012', '\141', '\126', + '\155', '\040', '\141', '\156', '\040', '\061', '\012', '\144', + '\156', '\122', '\040', '\141', '\156', '\040', '\061', '\012', + '\164', '\170', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\172', '\151', '\040', '\151', '\156', '\040', + '\061', '\012', '\172', '\170', '\126', '\040', '\163', '\172', + '\040', '\061', '\012', '\170', '\147', '\121', '\040', '\156', + '\147', '\040', '\061', '\012', '\164', '\166', '\132', '\040', + '\164', '\150', '\040', '\061', '\012', '\152', '\167', '\116', + '\040', '\151', '\152', '\040', '\061', '\012', '\105', '\161', + '\152', '\040', '\161', '\165', '\040', '\061', '\012', '\102', + '\170', '\152', '\040', '\151', '\152', '\040', '\061', '\012', + '\150', '\172', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\146', '\171', '\040', '\156', '\171', '\040', + '\061', '\012', '\120', '\160', '\152', '\040', '\151', '\152', + '\040', '\061', '\012', '\101', '\161', '\160', '\040', '\161', + '\165', '\040', '\061', '\012', '\172', '\112', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\163', '\172', '\106', + '\040', '\163', '\164', '\040', '\061', '\012', '\161', '\146', + '\130', '\040', '\161', '\165', '\040', '\061', '\012', '\160', + '\172', '\126', '\040', '\163', '\172', '\040', '\061', '\012', + '\164', '\147', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\163', '\123', '\040', '\163', '\172', '\040', + '\061', '\012', '\156', '\121', '\172', '\040', '\141', '\156', + '\040', '\061', '\012', '\164', '\153', '\106', '\040', '\164', + '\150', '\040', '\061', '\012', '\121', '\150', '\161', '\040', + '\164', '\150', '\040', '\061', '\012', '\147', '\112', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\165', '\117', + '\141', '\040', '\141', '\156', '\040', '\061', '\012', '\162', + '\161', '\127', '\040', '\161', '\165', '\040', '\061', '\012', + '\146', '\131', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\165', '\106', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\116', '\143', '\170', '\040', '\143', '\150', + '\040', '\061', '\012', '\154', '\115', '\167', '\040', '\154', + '\145', '\040', '\061', '\012', '\143', '\152', '\111', '\040', + '\143', '\150', '\040', '\061', '\012', '\112', '\143', '\167', + '\040', '\143', '\150', '\040', '\061', '\012', '\166', '\105', + '\157', '\040', '\157', '\156', '\040', '\061', '\012', '\145', + '\121', '\171', '\040', '\145', '\162', '\040', '\061', '\012', + '\123', '\170', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\125', '\170', '\040', '\155', '\142', '\040', + '\061', '\012', '\172', '\144', '\112', '\040', '\163', '\172', + '\040', '\061', '\012', '\154', '\160', '\116', '\040', '\154', + '\145', '\040', '\061', '\012', '\122', '\153', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\166', '\166', '\111', + '\040', '\166', '\141', '\040', '\061', '\012', '\121', '\155', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\164', + '\147', '\112', '\040', '\164', '\150', '\040', '\061', '\012', + '\147', '\146', '\105', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\143', '\130', '\040', '\143', '\150', '\040', + '\061', '\012', '\153', '\154', '\124', '\040', '\154', '\145', + '\040', '\061', '\012', '\142', '\142', '\126', '\040', '\142', + '\145', '\040', '\061', '\012', '\160', '\155', '\132', '\040', + '\155', '\145', '\040', '\061', '\012', '\165', '\161', '\101', + '\040', '\161', '\165', '\040', '\061', '\012', '\143', '\131', + '\171', '\040', '\143', '\150', '\040', '\061', '\012', '\167', + '\155', '\131', '\040', '\155', '\145', '\040', '\061', '\012', + '\172', '\154', '\102', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\116', '\144', '\040', '\163', '\172', '\040', + '\061', '\012', '\143', '\166', '\132', '\040', '\143', '\150', + '\040', '\061', '\012', '\144', '\166', '\114', '\040', '\144', + '\145', '\040', '\061', '\012', '\167', '\114', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\161', '\143', '\107', + '\040', '\143', '\150', '\040', '\061', '\012', '\121', '\152', + '\154', '\040', '\154', '\145', '\040', '\061', '\012', '\156', + '\161', '\146', '\040', '\141', '\156', '\040', '\061', '\012', + '\147', '\170', '\131', '\040', '\156', '\147', '\040', '\061', + '\012', '\141', '\161', '\111', '\040', '\141', '\156', '\040', + '\061', '\012', '\113', '\161', '\141', '\040', '\141', '\156', + '\040', '\061', '\012', '\130', '\161', '\160', '\040', '\161', + '\165', '\040', '\061', '\012', '\131', '\166', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\161', '\161', '\106', + '\040', '\161', '\165', '\040', '\061', '\012', '\171', '\110', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\156', + '\110', '\143', '\040', '\141', '\156', '\040', '\061', '\012', + '\125', '\161', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\146', '\116', '\040', '\163', '\172', '\040', + '\061', '\012', '\155', '\130', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\106', '\147', '\152', '\040', '\156', + '\147', '\040', '\061', '\012', '\104', '\163', '\170', '\040', + '\163', '\172', '\040', '\061', '\012', '\170', '\122', '\166', + '\040', '\166', '\141', '\040', '\061', '\012', '\167', '\142', + '\132', '\040', '\167', '\141', '\040', '\061', '\012', '\110', + '\156', '\160', '\040', '\141', '\156', '\040', '\061', '\012', + '\146', '\125', '\170', '\040', '\146', '\157', '\040', '\061', + '\012', '\143', '\131', '\144', '\040', '\143', '\150', '\040', + '\061', '\012', '\161', '\124', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\102', '\147', '\161', '\040', '\156', + '\147', '\040', '\061', '\012', '\160', '\103', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\130', '\155', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\166', '\152', + '\112', '\040', '\151', '\152', '\040', '\061', '\012', '\164', + '\144', '\107', '\040', '\164', '\150', '\040', '\061', '\012', + '\132', '\150', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\106', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\144', '\153', '\121', '\040', '\144', '\145', + '\040', '\061', '\012', '\114', '\143', '\147', '\040', '\143', + '\150', '\040', '\061', '\012', '\155', '\111', '\165', '\040', + '\161', '\165', '\040', '\061', '\012', '\111', '\167', '\144', + '\040', '\144', '\145', '\040', '\061', '\012', '\167', '\152', + '\167', '\040', '\151', '\152', '\040', '\061', '\012', '\172', + '\142', '\130', '\040', '\163', '\172', '\040', '\061', '\012', + '\131', '\150', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\166', '\110', '\040', '\143', '\150', '\040', + '\061', '\012', '\114', '\143', '\170', '\040', '\143', '\150', + '\040', '\061', '\012', '\127', '\146', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\116', '\146', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\161', '\115', '\166', + '\040', '\161', '\165', '\040', '\061', '\012', '\125', '\166', + '\167', '\040', '\166', '\141', '\040', '\061', '\012', '\121', + '\156', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\156', '\142', '\107', '\040', '\141', '\156', '\040', '\061', + '\012', '\163', '\106', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\170', '\154', '\112', '\040', '\154', '\145', + '\040', '\061', '\012', '\142', '\120', '\142', '\040', '\142', + '\145', '\040', '\061', '\012', '\170', '\160', '\111', '\040', + '\160', '\162', '\040', '\061', '\012', '\155', '\162', '\126', + '\040', '\145', '\162', '\040', '\061', '\012', '\106', '\167', + '\165', '\040', '\161', '\165', '\040', '\061', '\012', '\167', + '\117', '\171', '\040', '\167', '\141', '\040', '\061', '\012', + '\120', '\155', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\112', '\150', '\161', '\040', '\164', '\150', '\040', + '\061', '\012', '\132', '\142', '\170', '\040', '\142', '\145', + '\040', '\061', '\012', '\160', '\147', '\131', '\040', '\156', + '\147', '\040', '\061', '\012', '\122', '\142', '\167', '\040', + '\167', '\141', '\040', '\061', '\012', '\101', '\167', '\170', + '\040', '\167', '\141', '\040', '\061', '\012', '\155', '\143', + '\102', '\040', '\143', '\150', '\040', '\061', '\012', '\147', + '\153', '\107', '\040', '\156', '\147', '\040', '\061', '\012', + '\170', '\153', '\127', '\040', '\153', '\141', '\040', '\061', + '\012', '\120', '\156', '\167', '\040', '\151', '\156', '\040', + '\061', '\012', '\142', '\116', '\163', '\040', '\163', '\172', + '\040', '\061', '\012', '\156', '\130', '\162', '\040', '\141', + '\156', '\040', '\061', '\012', '\126', '\155', '\164', '\040', + '\164', '\150', '\040', '\061', '\012', '\145', '\125', '\166', + '\040', '\145', '\162', '\040', '\061', '\012', '\171', '\121', + '\166', '\040', '\166', '\141', '\040', '\061', '\012', '\153', + '\170', '\162', '\040', '\145', '\162', '\040', '\061', '\012', + '\113', '\163', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\160', '\127', '\040', '\160', '\162', '\040', + '\061', '\012', '\161', '\145', '\104', '\040', '\161', '\165', + '\040', '\061', '\012', '\121', '\166', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\142', '\122', '\155', '\040', + '\155', '\145', '\040', '\061', '\012', '\161', '\112', '\155', + '\040', '\161', '\165', '\040', '\061', '\012', '\143', '\163', + '\131', '\040', '\143', '\150', '\040', '\061', '\012', '\161', + '\167', '\110', '\040', '\161', '\165', '\040', '\061', '\012', + '\103', '\161', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\131', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\144', '\120', '\160', '\040', '\144', '\145', + '\040', '\061', '\012', '\157', '\101', '\145', '\040', '\145', + '\162', '\040', '\061', '\012', '\144', '\143', '\123', '\040', + '\143', '\150', '\040', '\061', '\012', '\165', '\167', '\125', + '\040', '\161', '\165', '\040', '\061', '\012', '\172', '\152', + '\114', '\040', '\163', '\172', '\040', '\061', '\012', '\157', + '\132', '\170', '\040', '\157', '\156', '\040', '\061', '\012', + '\153', '\152', '\122', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\104', '\171', '\040', '\143', '\150', '\040', + '\061', '\012', '\146', '\123', '\163', '\040', '\163', '\172', + '\040', '\061', '\012', '\145', '\121', '\146', '\040', '\154', + '\145', '\040', '\061', '\012', '\161', '\102', '\155', '\040', + '\161', '\165', '\040', '\061', '\012', '\155', '\114', '\142', + '\040', '\155', '\145', '\040', '\061', '\012', '\132', '\162', + '\152', '\040', '\145', '\162', '\040', '\061', '\012', '\107', + '\153', '\170', '\040', '\153', '\141', '\040', '\061', '\012', + '\160', '\153', '\130', '\040', '\153', '\141', '\040', '\061', + '\012', '\166', '\124', '\153', '\040', '\153', '\141', '\040', + '\061', '\012', '\132', '\147', '\160', '\040', '\156', '\147', + '\040', '\061', '\012', '\144', '\150', '\120', '\040', '\164', + '\150', '\040', '\061', '\012', '\156', '\120', '\166', '\040', + '\141', '\156', '\040', '\061', '\012', '\170', '\156', '\121', + '\040', '\141', '\156', '\040', '\061', '\012', '\142', '\110', + '\160', '\040', '\160', '\162', '\040', '\061', '\012', '\130', + '\147', '\146', '\040', '\156', '\147', '\040', '\061', '\012', + '\103', '\167', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\154', '\142', '\116', '\040', '\154', '\145', '\040', + '\061', '\012', '\152', '\116', '\155', '\040', '\151', '\152', + '\040', '\061', '\012', '\170', '\116', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\162', '\112', '\160', '\040', + '\145', '\162', '\040', '\061', '\012', '\157', '\112', '\144', + '\040', '\157', '\156', '\040', '\061', '\012', '\122', '\171', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\154', + '\166', '\114', '\040', '\154', '\145', '\040', '\061', '\012', + '\161', '\166', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\167', '\103', '\040', '\166', '\141', '\040', + '\061', '\012', '\153', '\106', '\152', '\040', '\151', '\152', + '\040', '\061', '\012', '\161', '\110', '\144', '\040', '\161', + '\165', '\040', '\061', '\012', '\167', '\143', '\102', '\040', + '\143', '\150', '\040', '\061', '\012', '\170', '\124', '\163', + '\040', '\163', '\172', '\040', '\061', '\012', '\146', '\121', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\104', + '\154', '\146', '\040', '\154', '\145', '\040', '\061', '\012', + '\167', '\114', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\106', '\142', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\162', '\161', '\112', '\040', '\161', '\165', + '\040', '\061', '\012', '\150', '\150', '\117', '\040', '\164', + '\150', '\040', '\061', '\012', '\170', '\117', '\151', '\040', + '\151', '\156', '\040', '\061', '\012', '\155', '\161', '\172', + '\040', '\161', '\165', '\040', '\061', '\012', '\161', '\155', + '\121', '\040', '\155', '\145', '\040', '\061', '\012', '\161', + '\121', '\152', '\040', '\161', '\165', '\040', '\061', '\012', + '\157', '\166', '\121', '\040', '\157', '\156', '\040', '\061', + '\012', '\147', '\146', '\122', '\040', '\156', '\147', '\040', + '\061', '\012', '\120', '\155', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\124', '\143', '\152', '\040', '\143', + '\150', '\040', '\061', '\012', '\155', '\161', '\121', '\040', + '\161', '\165', '\040', '\061', '\012', '\155', '\167', '\126', + '\040', '\155', '\145', '\040', '\061', '\012', '\142', '\130', + '\167', '\040', '\167', '\141', '\040', '\061', '\012', '\152', + '\154', '\101', '\040', '\154', '\145', '\040', '\061', '\012', + '\146', '\152', '\107', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\170', '\131', '\040', '\151', '\152', '\040', + '\061', '\012', '\161', '\167', '\115', '\040', '\161', '\165', + '\040', '\061', '\012', '\153', '\166', '\125', '\040', '\153', + '\141', '\040', '\061', '\012', '\102', '\153', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\147', '\146', '\101', + '\040', '\156', '\147', '\040', '\061', '\012', '\101', '\167', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\126', + '\155', '\166', '\040', '\166', '\141', '\040', '\061', '\012', + '\121', '\150', '\154', '\040', '\164', '\150', '\040', '\061', + '\012', '\127', '\155', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\143', '\115', '\161', '\040', '\143', '\150', + '\040', '\061', '\012', '\164', '\110', '\160', '\040', '\164', + '\150', '\040', '\061', '\012', '\154', '\120', '\142', '\040', + '\154', '\145', '\040', '\061', '\012', '\166', '\154', '\113', + '\040', '\154', '\145', '\040', '\061', '\012', '\131', '\147', + '\153', '\040', '\156', '\147', '\040', '\061', '\012', '\147', + '\112', '\163', '\040', '\156', '\147', '\040', '\061', '\012', + '\164', '\127', '\154', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\126', '\167', '\040', '\167', '\141', '\040', + '\061', '\012', '\163', '\162', '\116', '\040', '\145', '\162', + '\040', '\061', '\012', '\125', '\150', '\142', '\040', '\164', + '\150', '\040', '\061', '\012', '\166', '\146', '\122', '\040', + '\166', '\141', '\040', '\061', '\012', '\153', '\106', '\146', + '\040', '\153', '\141', '\040', '\061', '\012', '\112', '\154', + '\172', '\040', '\154', '\145', '\040', '\061', '\012', '\146', + '\113', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\155', '\122', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\127', '\167', '\040', '\153', '\141', '\040', + '\061', '\012', '\172', '\166', '\117', '\040', '\163', '\172', + '\040', '\061', '\012', '\130', '\161', '\172', '\040', '\161', + '\165', '\040', '\061', '\012', '\144', '\111', '\152', '\040', + '\144', '\145', '\040', '\061', '\012', '\167', '\112', '\155', + '\040', '\155', '\145', '\040', '\061', '\012', '\106', '\161', + '\166', '\040', '\161', '\165', '\040', '\061', '\012', '\167', + '\116', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\154', '\170', '\114', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\114', '\155', '\040', '\155', '\145', '\040', + '\061', '\012', '\144', '\161', '\116', '\040', '\161', '\165', + '\040', '\061', '\012', '\167', '\122', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\114', '\152', '\164', '\040', + '\164', '\150', '\040', '\061', '\012', '\167', '\122', '\167', + '\040', '\167', '\141', '\040', '\061', '\012', '\143', '\170', + '\102', '\040', '\143', '\150', '\040', '\061', '\012', '\143', + '\152', '\110', '\040', '\143', '\150', '\040', '\061', '\012', + '\126', '\161', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\112', '\163', '\040', '\161', '\165', '\040', + '\061', '\012', '\143', '\106', '\153', '\040', '\143', '\150', + '\040', '\061', '\012', '\170', '\161', '\144', '\040', '\161', + '\165', '\040', '\061', '\012', '\105', '\161', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\161', '\122', '\144', + '\040', '\161', '\165', '\040', '\061', '\012', '\166', '\146', + '\124', '\040', '\166', '\141', '\040', '\061', '\012', '\132', + '\161', '\142', '\040', '\161', '\165', '\040', '\061', '\012', + '\155', '\107', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\123', '\142', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\151', '\167', '\126', '\040', '\151', '\156', + '\040', '\061', '\012', '\152', '\146', '\111', '\040', '\151', + '\152', '\040', '\061', '\012', '\156', '\127', '\172', '\040', + '\141', '\156', '\040', '\061', '\012', '\114', '\152', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\162', '\152', + '\107', '\040', '\145', '\162', '\040', '\061', '\012', '\143', + '\106', '\142', '\040', '\143', '\150', '\040', '\061', '\012', + '\165', '\161', '\132', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\126', '\155', '\040', '\155', '\145', '\040', + '\061', '\012', '\152', '\147', '\113', '\040', '\156', '\147', + '\040', '\061', '\012', '\144', '\132', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\102', '\161', '\170', '\040', + '\161', '\165', '\040', '\061', '\012', '\161', '\165', '\107', + '\040', '\165', '\156', '\040', '\061', '\012', '\154', '\103', + '\166', '\040', '\154', '\145', '\040', '\061', '\012', '\154', + '\170', '\127', '\040', '\154', '\145', '\040', '\061', '\012', + '\147', '\107', '\142', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\166', '\131', '\040', '\156', '\147', '\040', + '\061', '\012', '\155', '\152', '\106', '\040', '\151', '\152', + '\040', '\061', '\012', '\160', '\164', '\130', '\040', '\164', + '\150', '\040', '\061', '\012', '\160', '\131', '\171', '\040', + '\160', '\162', '\040', '\061', '\012', '\131', '\162', '\146', + '\040', '\145', '\162', '\040', '\061', '\012', '\155', '\126', + '\144', '\040', '\144', '\145', '\040', '\061', '\012', '\172', + '\160', '\122', '\040', '\163', '\172', '\040', '\061', '\012', + '\170', '\113', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\167', '\160', '\115', '\040', '\160', '\162', '\040', + '\061', '\012', '\143', '\114', '\153', '\040', '\143', '\150', + '\040', '\061', '\012', '\123', '\161', '\172', '\040', '\161', + '\165', '\040', '\061', '\012', '\147', '\127', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\163', '\127', '\172', + '\040', '\163', '\164', '\040', '\061', '\012', '\163', '\162', + '\123', '\040', '\145', '\162', '\040', '\061', '\012', '\143', + '\126', '\170', '\040', '\143', '\150', '\040', '\061', '\012', + '\170', '\116', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\150', '\120', '\142', '\040', '\164', '\150', '\040', + '\061', '\012', '\142', '\107', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\164', '\144', '\110', '\040', '\164', + '\150', '\040', '\061', '\012', '\171', '\112', '\154', '\040', + '\154', '\145', '\040', '\061', '\012', '\166', '\125', '\153', + '\040', '\153', '\141', '\040', '\061', '\012', '\144', '\112', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\161', + '\150', '\111', '\040', '\164', '\150', '\040', '\061', '\012', + '\155', '\164', '\120', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\107', '\142', '\040', '\154', '\145', '\040', + '\061', '\012', '\150', '\104', '\170', '\040', '\164', '\150', + '\040', '\061', '\012', '\172', '\146', '\127', '\040', '\163', + '\172', '\040', '\061', '\012', '\116', '\155', '\154', '\040', + '\154', '\145', '\040', '\061', '\012', '\110', '\163', '\167', + '\040', '\163', '\164', '\040', '\061', '\012', '\160', '\146', + '\107', '\040', '\160', '\162', '\040', '\061', '\012', '\144', + '\115', '\152', '\040', '\144', '\145', '\040', '\061', '\012', + '\153', '\113', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\152', '\123', '\040', '\145', '\162', '\040', + '\061', '\012', '\121', '\154', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\116', '\146', '\171', '\040', '\156', + '\171', '\040', '\061', '\012', '\143', '\161', '\115', '\040', + '\143', '\150', '\040', '\061', '\012', '\150', '\127', '\155', + '\040', '\164', '\150', '\040', '\061', '\012', '\146', '\165', + '\117', '\040', '\161', '\165', '\040', '\061', '\012', '\172', + '\146', '\106', '\040', '\163', '\172', '\040', '\061', '\012', + '\161', '\147', '\110', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\160', '\132', '\040', '\160', '\162', '\040', + '\061', '\012', '\142', '\164', '\131', '\040', '\164', '\150', + '\040', '\061', '\012', '\165', '\161', '\102', '\040', '\161', + '\165', '\040', '\061', '\012', '\161', '\171', '\101', '\040', + '\161', '\165', '\040', '\061', '\012', '\130', '\162', '\160', + '\040', '\145', '\162', '\040', '\061', '\012', '\171', '\164', + '\130', '\040', '\164', '\150', '\040', '\061', '\012', '\144', + '\110', '\155', '\040', '\144', '\145', '\040', '\061', '\012', + '\166', '\102', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\171', '\171', '\116', '\040', '\156', '\171', '\040', + '\061', '\012', '\121', '\162', '\152', '\040', '\145', '\162', + '\040', '\061', '\012', '\147', '\113', '\144', '\040', '\156', + '\147', '\040', '\061', '\012', '\142', '\146', '\125', '\040', + '\142', '\145', '\040', '\061', '\012', '\121', '\146', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\142', '\161', + '\120', '\040', '\161', '\165', '\040', '\061', '\012', '\161', + '\117', '\172', '\040', '\161', '\165', '\040', '\061', '\012', + '\130', '\150', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\161', '\131', '\040', '\161', '\165', '\040', + '\061', '\012', '\150', '\152', '\121', '\040', '\164', '\150', + '\040', '\061', '\012', '\131', '\146', '\165', '\040', '\161', + '\165', '\040', '\061', '\012', '\141', '\130', '\153', '\040', + '\141', '\156', '\040', '\061', '\012', '\160', '\142', '\126', + '\040', '\160', '\162', '\040', '\061', '\012', '\166', '\152', + '\120', '\040', '\151', '\152', '\040', '\061', '\012', '\131', + '\142', '\160', '\040', '\160', '\162', '\040', '\061', '\012', + '\112', '\155', '\142', '\040', '\155', '\145', '\040', '\061', + '\012', '\161', '\106', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\171', '\120', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\171', '\127', '\167', '\040', '\167', + '\141', '\040', '\061', '\012', '\166', '\150', '\130', '\040', + '\164', '\150', '\040', '\061', '\012', '\151', '\167', '\124', + '\040', '\151', '\156', '\040', '\061', '\012', '\161', '\132', + '\146', '\040', '\161', '\165', '\040', '\061', '\012', '\165', + '\161', '\125', '\040', '\161', '\165', '\040', '\061', '\012', + '\165', '\106', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\160', '\127', '\040', '\143', '\150', '\040', + '\061', '\012', '\114', '\160', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\153', '\146', '\114', '\040', '\153', + '\141', '\040', '\061', '\012', '\160', '\121', '\145', '\040', + '\145', '\162', '\040', '\061', '\012', '\147', '\167', '\172', + '\040', '\156', '\147', '\040', '\061', '\012', '\152', '\160', + '\115', '\040', '\151', '\152', '\040', '\061', '\012', '\121', + '\153', '\155', '\040', '\153', '\141', '\040', '\061', '\012', + '\152', '\147', '\110', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\152', '\120', '\040', '\151', '\152', '\040', + '\061', '\012', '\170', '\147', '\114', '\040', '\156', '\147', + '\040', '\061', '\012', '\152', '\114', '\155', '\040', '\151', + '\152', '\040', '\061', '\012', '\144', '\170', '\116', '\040', + '\144', '\145', '\040', '\061', '\012', '\166', '\127', '\163', + '\040', '\163', '\164', '\040', '\061', '\012', '\112', '\152', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\150', + '\150', '\107', '\040', '\164', '\150', '\040', '\061', '\012', + '\131', '\166', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\162', '\105', '\040', '\145', '\162', '\040', + '\061', '\012', '\142', '\132', '\167', '\040', '\167', '\141', + '\040', '\061', '\012', '\114', '\166', '\167', '\040', '\166', + '\141', '\040', '\061', '\012', '\145', '\116', '\167', '\040', + '\145', '\162', '\040', '\061', '\012', '\146', '\152', '\102', + '\040', '\151', '\152', '\040', '\061', '\012', '\144', '\143', + '\121', '\040', '\143', '\150', '\040', '\061', '\012', '\154', + '\132', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\112', '\167', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\120', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\170', '\115', '\142', '\040', '\142', '\145', + '\040', '\061', '\012', '\150', '\146', '\104', '\040', '\164', + '\150', '\040', '\061', '\012', '\152', '\172', '\121', '\040', + '\163', '\172', '\040', '\061', '\012', '\125', '\165', '\146', + '\040', '\161', '\165', '\040', '\061', '\012', '\172', '\107', + '\153', '\040', '\163', '\172', '\040', '\061', '\012', '\172', + '\103', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\156', '\160', '\103', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\127', '\144', '\040', '\164', '\150', '\040', + '\061', '\012', '\150', '\152', '\106', '\040', '\164', '\150', + '\040', '\061', '\012', '\120', '\172', '\163', '\040', '\163', + '\164', '\040', '\061', '\012', '\167', '\165', '\101', '\040', + '\161', '\165', '\040', '\061', '\012', '\121', '\150', '\147', + '\040', '\164', '\150', '\040', '\061', '\012', '\115', '\161', + '\155', '\040', '\161', '\165', '\040', '\061', '\012', '\146', + '\163', '\111', '\040', '\163', '\164', '\040', '\061', '\012', + '\146', '\144', '\125', '\040', '\144', '\145', '\040', '\061', + '\012', '\130', '\162', '\155', '\040', '\145', '\162', '\040', + '\061', '\012', '\161', '\121', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\142', '\153', '\127', '\040', '\153', + '\141', '\040', '\061', '\012', '\144', '\110', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\162', '\143', '\102', + '\040', '\143', '\150', '\040', '\061', '\012', '\150', '\127', + '\165', '\040', '\164', '\150', '\040', '\061', '\012', '\156', + '\111', '\161', '\040', '\141', '\156', '\040', '\061', '\012', + '\162', '\131', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\130', '\166', '\040', '\166', '\141', '\040', + '\061', '\012', '\167', '\161', '\120', '\040', '\161', '\165', + '\040', '\061', '\012', '\170', '\155', '\116', '\040', '\155', + '\145', '\040', '\061', '\012', '\163', '\112', '\146', '\040', + '\163', '\164', '\040', '\061', '\012', '\171', '\115', '\146', + '\040', '\156', '\171', '\040', '\061', '\012', '\123', '\146', + '\153', '\040', '\153', '\141', '\040', '\061', '\012', '\161', + '\172', '\127', '\040', '\161', '\165', '\040', '\061', '\012', + '\143', '\166', '\124', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\155', '\130', '\040', '\153', '\141', '\040', + '\061', '\012', '\170', '\161', '\125', '\040', '\161', '\165', + '\040', '\061', '\012', '\143', '\156', '\107', '\040', '\141', + '\156', '\040', '\061', '\012', '\112', '\160', '\151', '\040', + '\151', '\156', '\040', '\061', '\012', '\146', '\162', '\130', + '\040', '\145', '\162', '\040', '\061', '\012', '\171', '\114', + '\146', '\040', '\156', '\171', '\040', '\061', '\012', '\165', + '\171', '\125', '\040', '\161', '\165', '\040', '\061', '\012', + '\104', '\144', '\167', '\040', '\144', '\145', '\040', '\061', + '\012', '\124', '\147', '\152', '\040', '\156', '\147', '\040', + '\061', '\012', '\161', '\145', '\110', '\040', '\161', '\165', + '\040', '\061', '\012', '\146', '\105', '\172', '\040', '\163', + '\172', '\040', '\061', '\012', '\160', '\103', '\153', '\040', + '\153', '\141', '\040', '\061', '\012', '\161', '\155', '\146', + '\040', '\161', '\165', '\040', '\061', '\012', '\162', '\152', + '\110', '\040', '\145', '\162', '\040', '\061', '\012', '\170', + '\115', '\160', '\040', '\160', '\162', '\040', '\061', '\012', + '\131', '\167', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\172', '\147', '\104', '\040', '\156', '\147', '\040', + '\061', '\012', '\120', '\161', '\170', '\040', '\161', '\165', + '\040', '\061', '\012', '\156', '\161', '\115', '\040', '\157', + '\156', '\040', '\061', '\012', '\167', '\144', '\130', '\040', + '\144', '\145', '\040', '\061', '\012', '\102', '\160', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\154', '\150', + '\115', '\040', '\164', '\150', '\040', '\061', '\012', '\105', + '\160', '\142', '\040', '\160', '\162', '\040', '\061', '\012', + '\142', '\150', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\166', '\121', '\040', '\153', '\141', '\040', + '\061', '\012', '\122', '\163', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\170', '\142', '\120', '\040', '\142', + '\145', '\040', '\061', '\012', '\156', '\115', '\155', '\040', + '\141', '\156', '\040', '\061', '\012', '\170', '\165', '\103', + '\040', '\161', '\165', '\040', '\061', '\012', '\167', '\152', + '\163', '\040', '\163', '\172', '\040', '\061', '\012', '\146', + '\170', '\130', '\040', '\146', '\157', '\040', '\061', '\012', + '\150', '\166', '\124', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\120', '\170', '\040', '\161', '\165', '\040', + '\061', '\012', '\112', '\155', '\171', '\040', '\155', '\145', + '\040', '\061', '\012', '\121', '\172', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\116', '\163', '\172', '\040', + '\163', '\164', '\040', '\061', '\012', '\166', '\127', '\144', + '\040', '\144', '\145', '\040', '\061', '\012', '\150', '\146', + '\130', '\040', '\164', '\150', '\040', '\061', '\012', '\152', + '\103', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\171', '\121', '\170', '\040', '\156', '\171', '\040', '\061', + '\012', '\167', '\150', '\112', '\040', '\164', '\150', '\040', + '\061', '\012', '\167', '\162', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\170', '\147', '\127', '\040', '\156', + '\147', '\040', '\061', '\012', '\112', '\150', '\152', '\040', + '\164', '\150', '\040', '\061', '\012', '\154', '\150', '\103', + '\040', '\164', '\150', '\040', '\061', '\012', '\120', '\167', + '\146', '\040', '\157', '\167', '\040', '\061', '\012', '\154', + '\152', '\103', '\040', '\154', '\145', '\040', '\061', '\012', + '\166', '\166', '\102', '\040', '\166', '\141', '\040', '\061', + '\012', '\155', '\143', '\116', '\040', '\143', '\150', '\040', + '\061', '\012', '\171', '\110', '\170', '\040', '\156', '\171', + '\040', '\061', '\012', '\142', '\102', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\161', '\122', '\172', '\040', + '\161', '\165', '\040', '\061', '\012', '\147', '\154', '\110', + '\040', '\156', '\147', '\040', '\061', '\012', '\143', '\132', + '\160', '\040', '\143', '\150', '\040', '\061', '\012', '\161', + '\112', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\164', '\123', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\126', '\155', '\040', '\155', '\145', '\040', + '\061', '\012', '\165', '\127', '\163', '\040', '\161', '\165', + '\040', '\061', '\012', '\126', '\170', '\157', '\040', '\157', + '\156', '\040', '\061', '\012', '\146', '\152', '\115', '\040', + '\151', '\152', '\040', '\061', '\012', '\172', '\150', '\113', + '\040', '\164', '\150', '\040', '\061', '\012', '\103', '\152', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\166', + '\132', '\162', '\040', '\145', '\162', '\040', '\061', '\012', + '\142', '\103', '\163', '\040', '\163', '\172', '\040', '\061', + '\012', '\162', '\167', '\131', '\040', '\145', '\162', '\040', + '\061', '\012', '\170', '\105', '\151', '\040', '\151', '\156', + '\040', '\061', '\012', '\144', '\125', '\166', '\040', '\144', + '\145', '\040', '\061', '\012', '\146', '\122', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\107', '\143', '\165', + '\040', '\143', '\150', '\040', '\061', '\012', '\152', '\104', + '\146', '\040', '\151', '\152', '\040', '\061', '\012', '\144', + '\152', '\110', '\040', '\144', '\145', '\040', '\061', '\012', + '\166', '\154', '\125', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\171', '\107', '\040', '\161', '\165', '\040', + '\061', '\012', '\153', '\146', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\154', '\130', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\154', '\142', '\103', '\040', + '\154', '\145', '\040', '\061', '\012', '\120', '\167', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\117', '\141', + '\145', '\040', '\141', '\156', '\040', '\061', '\012', '\160', + '\142', '\103', '\040', '\160', '\162', '\040', '\061', '\012', + '\144', '\127', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\172', '\125', '\040', '\154', '\145', '\040', + '\061', '\012', '\167', '\112', '\172', '\040', '\163', '\172', + '\040', '\061', '\012', '\144', '\131', '\152', '\040', '\144', + '\145', '\040', '\061', '\012', '\143', '\102', '\152', '\040', + '\143', '\150', '\040', '\061', '\012', '\146', '\122', '\166', + '\040', '\166', '\141', '\040', '\061', '\012', '\144', '\152', + '\107', '\040', '\144', '\145', '\040', '\061', '\012', '\155', + '\131', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\121', '\142', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\156', '\130', '\040', '\141', '\156', '\040', + '\061', '\012', '\167', '\120', '\155', '\040', '\155', '\145', + '\040', '\061', '\012', '\167', '\166', '\116', '\040', '\166', + '\141', '\040', '\061', '\012', '\161', '\107', '\155', '\040', + '\161', '\165', '\040', '\061', '\012', '\161', '\116', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\155', '\122', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\125', + '\161', '\166', '\040', '\161', '\165', '\040', '\061', '\012', + '\121', '\170', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\146', '\172', '\130', '\040', '\163', '\172', '\040', + '\061', '\012', '\172', '\152', '\115', '\040', '\163', '\172', + '\040', '\061', '\012', '\170', '\161', '\101', '\040', '\161', + '\165', '\040', '\061', '\012', '\142', '\115', '\163', '\040', + '\163', '\172', '\040', '\061', '\012', '\166', '\155', '\114', + '\040', '\155', '\145', '\040', '\061', '\012', '\105', '\171', + '\170', '\040', '\156', '\171', '\040', '\061', '\012', '\150', + '\110', '\152', '\040', '\164', '\150', '\040', '\061', '\012', + '\152', '\107', '\160', '\040', '\151', '\152', '\040', '\061', + '\012', '\155', '\146', '\104', '\040', '\155', '\145', '\040', + '\061', '\012', '\112', '\146', '\167', '\040', '\167', '\141', + '\040', '\061', '\012', '\127', '\152', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\142', '\132', '\163', '\040', + '\163', '\172', '\040', '\061', '\012', '\111', '\171', '\153', + '\040', '\153', '\141', '\040', '\061', '\012', '\172', '\122', + '\156', '\040', '\141', '\156', '\040', '\061', '\012', '\143', + '\144', '\125', '\040', '\143', '\150', '\040', '\061', '\012', + '\155', '\112', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\152', '\171', '\040', '\151', '\152', '\040', + '\061', '\012', '\121', '\141', '\157', '\040', '\141', '\156', + '\040', '\061', '\012', '\142', '\130', '\166', '\040', '\166', + '\141', '\040', '\061', '\012', '\150', '\123', '\147', '\040', + '\164', '\150', '\040', '\061', '\012', '\162', '\101', '\157', + '\040', '\145', '\162', '\040', '\061', '\012', '\150', '\114', + '\163', '\040', '\164', '\150', '\040', '\061', '\012', '\154', + '\103', '\163', '\040', '\154', '\145', '\040', '\061', '\012', + '\161', '\153', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\122', '\170', '\165', '\040', '\161', '\165', '\040', + '\061', '\012', '\170', '\144', '\116', '\040', '\144', '\145', + '\040', '\061', '\012', '\171', '\131', '\170', '\040', '\156', + '\171', '\040', '\061', '\012', '\144', '\153', '\116', '\040', + '\144', '\145', '\040', '\061', '\012', '\122', '\147', '\167', + '\040', '\156', '\147', '\040', '\061', '\012', '\172', '\147', + '\114', '\040', '\163', '\172', '\040', '\061', '\012', '\122', + '\143', '\152', '\040', '\143', '\150', '\040', '\061', '\012', + '\151', '\127', '\172', '\040', '\151', '\156', '\040', '\061', + '\012', '\144', '\114', '\153', '\040', '\144', '\145', '\040', + '\061', '\012', '\155', '\160', '\130', '\040', '\155', '\145', + '\040', '\061', '\012', '\107', '\142', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\142', '\156', '\110', '\040', + '\141', '\156', '\040', '\061', '\012', '\153', '\144', '\115', + '\040', '\144', '\145', '\040', '\061', '\012', '\167', '\161', + '\107', '\040', '\161', '\165', '\040', '\061', '\012', '\166', + '\115', '\172', '\040', '\163', '\172', '\040', '\061', '\012', + '\172', '\167', '\110', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\147', '\170', '\040', '\156', '\147', '\040', + '\061', '\012', '\114', '\152', '\153', '\040', '\151', '\152', + '\040', '\061', '\012', '\164', '\154', '\107', '\040', '\164', + '\150', '\040', '\061', '\012', '\164', '\147', '\105', '\040', + '\164', '\150', '\040', '\061', '\012', '\127', '\143', '\167', + '\040', '\143', '\150', '\040', '\061', '\012', '\126', '\142', + '\171', '\040', '\142', '\145', '\040', '\061', '\012', '\155', + '\126', '\172', '\040', '\163', '\172', '\040', '\061', '\012', + '\110', '\147', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\161', '\120', '\040', '\156', '\147', '\040', + '\061', '\012', '\150', '\150', '\102', '\040', '\164', '\150', + '\040', '\061', '\012', '\156', '\106', '\170', '\040', '\141', + '\156', '\040', '\061', '\012', '\171', '\102', '\146', '\040', + '\156', '\171', '\040', '\061', '\012', '\127', '\155', '\170', + '\040', '\155', '\145', '\040', '\061', '\012', '\166', '\116', + '\142', '\040', '\166', '\141', '\040', '\061', '\012', '\115', + '\156', '\166', '\040', '\141', '\156', '\040', '\061', '\012', + '\132', '\155', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\172', '\123', '\040', '\163', '\172', '\040', + '\061', '\012', '\171', '\146', '\103', '\040', '\156', '\171', + '\040', '\061', '\012', '\105', '\160', '\170', '\040', '\160', + '\162', '\040', '\061', '\012', '\154', '\152', '\107', '\040', + '\154', '\145', '\040', '\061', '\012', '\167', '\125', '\141', + '\040', '\141', '\156', '\040', '\061', '\012', '\121', '\147', + '\157', '\040', '\156', '\147', '\040', '\061', '\012', '\160', + '\161', '\142', '\040', '\161', '\165', '\040', '\061', '\012', + '\112', '\153', '\155', '\040', '\153', '\141', '\040', '\061', + '\012', '\127', '\166', '\171', '\040', '\166', '\141', '\040', + '\061', '\012', '\102', '\152', '\160', '\040', '\151', '\152', + '\040', '\061', '\012', '\166', '\146', '\132', '\040', '\166', + '\141', '\040', '\061', '\012', '\167', '\170', '\124', '\040', + '\167', '\141', '\040', '\061', '\012', '\126', '\170', '\167', + '\040', '\167', '\141', '\040', '\061', '\012', '\144', '\122', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\156', + '\126', '\161', '\040', '\141', '\156', '\040', '\061', '\012', + '\151', '\127', '\146', '\040', '\151', '\156', '\040', '\061', + '\012', '\123', '\155', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\152', '\167', '\107', '\040', '\151', '\152', + '\040', '\061', '\012', '\166', '\143', '\127', '\040', '\143', + '\150', '\040', '\061', '\012', '\121', '\147', '\172', '\040', + '\156', '\147', '\040', '\061', '\012', '\127', '\153', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\170', '\162', + '\114', '\040', '\145', '\162', '\040', '\061', '\012', '\164', + '\126', '\150', '\040', '\143', '\150', '\040', '\061', '\012', + '\132', '\154', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\172', '\104', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\171', '\170', '\120', '\040', '\156', '\171', + '\040', '\061', '\012', '\131', '\171', '\167', '\040', '\167', + '\141', '\040', '\061', '\012', '\172', '\120', '\153', '\040', + '\163', '\172', '\040', '\061', '\012', '\102', '\147', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\170', '\117', + '\153', '\040', '\153', '\141', '\040', '\061', '\012', '\157', + '\130', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\164', '\121', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\170', '\106', '\040', '\146', '\157', '\040', + '\061', '\012', '\144', '\117', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\126', '\164', '\160', '\040', '\164', + '\150', '\040', '\061', '\012', '\152', '\150', '\120', '\040', + '\164', '\150', '\040', '\061', '\012', '\166', '\150', '\132', + '\040', '\164', '\150', '\040', '\061', '\012', '\107', '\161', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\144', + '\106', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\145', '\103', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\152', '\110', '\040', '\151', '\152', '\040', + '\061', '\012', '\166', '\161', '\121', '\040', '\161', '\165', + '\040', '\061', '\012', '\152', '\160', '\114', '\040', '\151', + '\152', '\040', '\061', '\012', '\150', '\147', '\132', '\040', + '\164', '\150', '\040', '\061', '\012', '\170', '\106', '\144', + '\040', '\144', '\145', '\040', '\061', '\012', '\121', '\152', + '\144', '\040', '\144', '\145', '\040', '\061', '\012', '\170', + '\113', '\155', '\040', '\155', '\145', '\040', '\061', '\012', + '\172', '\121', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\116', '\150', '\167', '\040', '\164', '\150', '\040', + '\061', '\012', '\113', '\161', '\157', '\040', '\161', '\165', + '\040', '\061', '\012', '\150', '\167', '\117', '\040', '\164', + '\150', '\040', '\061', '\012', '\157', '\131', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\127', '\156', '\146', + '\040', '\141', '\156', '\040', '\061', '\012', '\166', '\123', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\101', + '\146', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\152', '\161', '\112', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\105', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\144', '\113', '\160', '\040', '\144', '\145', + '\040', '\061', '\012', '\156', '\155', '\113', '\040', '\141', + '\156', '\040', '\061', '\012', '\167', '\130', '\167', '\040', + '\167', '\141', '\040', '\061', '\012', '\166', '\152', '\103', + '\040', '\151', '\152', '\040', '\061', '\012', '\144', '\130', + '\142', '\040', '\144', '\145', '\040', '\061', '\012', '\164', + '\121', '\156', '\040', '\164', '\150', '\040', '\061', '\012', + '\161', '\157', '\122', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\122', '\146', '\040', '\142', '\145', '\040', + '\061', '\012', '\171', '\171', '\114', '\040', '\156', '\171', + '\040', '\061', '\012', '\153', '\123', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\130', '\171', '\165', '\040', + '\161', '\165', '\040', '\061', '\012', '\166', '\155', '\101', + '\040', '\166', '\141', '\040', '\061', '\012', '\132', '\147', + '\155', '\040', '\156', '\147', '\040', '\061', '\012', '\114', + '\142', '\170', '\040', '\142', '\145', '\040', '\061', '\012', + '\142', '\111', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\132', '\144', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\147', '\110', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\142', '\131', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\115', '\161', '\144', '\040', + '\161', '\165', '\040', '\061', '\012', '\161', '\115', '\153', + '\040', '\161', '\165', '\040', '\061', '\012', '\121', '\163', + '\166', '\040', '\163', '\164', '\040', '\061', '\012', '\172', + '\130', '\170', '\040', '\163', '\172', '\040', '\061', '\012', + '\150', '\121', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\143', '\126', '\040', '\143', '\150', '\040', + '\061', '\012', '\130', '\146', '\172', '\040', '\163', '\172', + '\040', '\061', '\012', '\115', '\150', '\143', '\040', '\164', + '\150', '\040', '\061', '\012', '\153', '\102', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\142', '\127', '\160', + '\040', '\160', '\162', '\040', '\061', '\012', '\127', '\172', + '\165', '\040', '\161', '\165', '\040', '\061', '\012', '\150', + '\127', '\167', '\040', '\164', '\150', '\040', '\061', '\012', + '\171', '\116', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\170', '\142', '\132', '\040', '\142', '\145', '\040', + '\061', '\012', '\155', '\124', '\142', '\040', '\155', '\145', + '\040', '\061', '\012', '\113', '\144', '\146', '\040', '\144', + '\145', '\040', '\061', '\012', '\160', '\146', '\121', '\040', + '\160', '\162', '\040', '\061', '\012', '\166', '\103', '\144', + '\040', '\144', '\145', '\040', '\061', '\012', '\120', '\161', + '\146', '\040', '\161', '\165', '\040', '\061', '\012', '\157', + '\146', '\132', '\040', '\157', '\156', '\040', '\061', '\012', + '\167', '\131', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\124', '\146', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\107', '\156', '\142', '\040', '\141', '\156', + '\040', '\061', '\012', '\132', '\144', '\170', '\040', '\144', + '\145', '\040', '\061', '\012', '\172', '\126', '\152', '\040', + '\163', '\172', '\040', '\061', '\012', '\124', '\161', '\167', + '\040', '\161', '\165', '\040', '\061', '\012', '\146', '\172', + '\126', '\040', '\163', '\172', '\040', '\061', '\012', '\111', + '\147', '\161', '\040', '\156', '\147', '\040', '\061', '\012', + '\121', '\166', '\166', '\040', '\166', '\151', '\040', '\061', + '\012', '\120', '\155', '\146', '\040', '\155', '\145', '\040', + '\061', '\012', '\161', '\110', '\145', '\040', '\161', '\165', + '\040', '\061', '\012', '\171', '\142', '\122', '\040', '\142', + '\145', '\040', '\061', '\012', '\143', '\106', '\147', '\040', + '\143', '\150', '\040', '\061', '\012', '\113', '\166', '\146', + '\040', '\166', '\141', '\040', '\061', '\012', '\132', '\170', + '\155', '\040', '\155', '\145', '\040', '\061', '\012', '\157', + '\126', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\131', '\150', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\167', '\120', '\040', '\167', '\141', '\040', + '\061', '\012', '\126', '\166', '\172', '\040', '\163', '\172', + '\040', '\061', '\012', '\163', '\144', '\127', '\040', '\144', + '\145', '\040', '\061', '\012', '\147', '\106', '\172', '\040', + '\156', '\147', '\040', '\061', '\012', '\155', '\122', '\154', + '\040', '\154', '\145', '\040', '\061', '\012', '\142', '\161', + '\116', '\040', '\161', '\165', '\040', '\061', '\012', '\142', + '\150', '\125', '\040', '\164', '\150', '\040', '\061', '\012', + '\164', '\102', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\110', '\142', '\142', '\040', '\142', '\145', '\040', + '\061', '\012', '\112', '\172', '\160', '\040', '\163', '\172', + '\040', '\061', '\012', '\172', '\162', '\123', '\040', '\145', + '\162', '\040', '\061', '\012', '\155', '\153', '\132', '\040', + '\155', '\145', '\040', '\061', '\012', '\142', '\113', '\167', + '\040', '\167', '\141', '\040', '\061', '\012', '\152', '\120', + '\170', '\040', '\151', '\152', '\040', '\061', '\012', '\130', + '\161', '\141', '\040', '\141', '\156', '\040', '\061', '\012', + '\146', '\107', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\114', '\153', '\040', '\153', '\141', '\040', + '\061', '\012', '\156', '\162', '\126', '\040', '\141', '\156', + '\040', '\061', '\012', '\124', '\155', '\170', '\040', '\155', + '\145', '\040', '\061', '\012', '\172', '\166', '\132', '\040', + '\163', '\172', '\040', '\061', '\012', '\147', '\127', '\154', + '\040', '\156', '\147', '\040', '\061', '\012', '\131', '\170', + '\142', '\040', '\142', '\145', '\040', '\061', '\012', '\171', + '\127', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\154', '\161', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\127', '\165', '\040', '\164', '\150', '\040', + '\061', '\012', '\170', '\132', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\151', '\161', '\111', '\040', '\151', + '\156', '\040', '\061', '\012', '\143', '\160', '\121', '\040', + '\143', '\150', '\040', '\061', '\012', '\172', '\120', '\146', + '\040', '\163', '\172', '\040', '\061', '\012', '\142', '\161', + '\107', '\040', '\161', '\165', '\040', '\061', '\012', '\147', + '\155', '\111', '\040', '\156', '\147', '\040', '\061', '\012', + '\127', '\153', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\132', '\166', '\163', '\040', '\163', '\172', '\040', + '\061', '\012', '\161', '\144', '\116', '\040', '\161', '\165', + '\040', '\061', '\012', '\150', '\131', '\146', '\040', '\164', + '\150', '\040', '\061', '\012', '\163', '\102', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\104', '\167', '\142', + '\040', '\157', '\167', '\040', '\061', '\012', '\127', '\172', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\121', + '\144', '\167', '\040', '\144', '\145', '\040', '\061', '\012', + '\163', '\166', '\122', '\040', '\163', '\172', '\040', '\061', + '\012', '\116', '\166', '\166', '\040', '\166', '\141', '\040', + '\061', '\012', '\152', '\122', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\161', '\104', '\166', '\040', '\161', + '\165', '\040', '\061', '\012', '\161', '\107', '\145', '\040', + '\161', '\165', '\040', '\061', '\012', '\143', '\167', '\124', + '\040', '\143', '\150', '\040', '\061', '\012', '\146', '\124', + '\171', '\040', '\156', '\171', '\040', '\061', '\012', '\103', + '\166', '\166', '\040', '\166', '\141', '\040', '\061', '\012', + '\146', '\154', '\121', '\040', '\154', '\145', '\040', '\061', + '\012', '\155', '\127', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\164', '\167', '\123', '\040', '\164', '\150', + '\040', '\061', '\012', '\156', '\160', '\115', '\040', '\141', + '\156', '\040', '\061', '\012', '\125', '\146', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\146', '\165', '\107', + '\040', '\161', '\165', '\040', '\061', '\012', '\157', '\103', + '\152', '\040', '\157', '\156', '\040', '\061', '\012', '\164', + '\170', '\106', '\040', '\164', '\150', '\040', '\061', '\012', + '\131', '\146', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\167', '\171', '\040', '\161', '\165', '\040', + '\061', '\012', '\126', '\144', '\172', '\040', '\144', '\145', + '\040', '\061', '\012', '\126', '\147', '\161', '\040', '\156', + '\147', '\040', '\061', '\012', '\122', '\153', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\120', '\170', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\155', '\103', + '\156', '\040', '\141', '\156', '\040', '\061', '\012', '\167', + '\150', '\132', '\040', '\164', '\150', '\040', '\061', '\012', + '\146', '\147', '\102', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\166', '\127', '\040', '\151', '\152', '\040', + '\061', '\012', '\153', '\144', '\114', '\040', '\144', '\145', + '\040', '\061', '\012', '\114', '\170', '\151', '\040', '\151', + '\156', '\040', '\061', '\012', '\163', '\166', '\102', '\040', + '\163', '\172', '\040', '\061', '\012', '\170', '\165', '\110', + '\040', '\161', '\165', '\040', '\061', '\012', '\147', '\106', + '\171', '\040', '\156', '\147', '\040', '\061', '\012', '\157', + '\126', '\166', '\040', '\157', '\156', '\040', '\061', '\012', + '\132', '\150', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\157', '\161', '\107', '\040', '\161', '\165', '\040', + '\061', '\012', '\157', '\112', '\160', '\040', '\157', '\156', + '\040', '\061', '\012', '\147', '\111', '\146', '\040', '\156', + '\147', '\040', '\061', '\012', '\142', '\167', '\106', '\040', + '\167', '\141', '\040', '\061', '\012', '\166', '\114', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\152', '\147', + '\130', '\040', '\156', '\147', '\040', '\061', '\012', '\161', + '\113', '\151', '\040', '\151', '\156', '\040', '\061', '\012', + '\170', '\122', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\167', '\126', '\040', '\161', '\165', '\040', + '\061', '\012', '\155', '\116', '\154', '\040', '\154', '\145', + '\040', '\061', '\012', '\107', '\166', '\166', '\040', '\166', + '\141', '\040', '\061', '\012', '\160', '\121', '\146', '\040', + '\160', '\162', '\040', '\061', '\012', '\170', '\142', '\126', + '\040', '\142', '\145', '\040', '\061', '\012', '\144', '\160', + '\132', '\040', '\144', '\145', '\040', '\061', '\012', '\146', + '\110', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\142', '\102', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\125', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\150', '\172', '\101', '\040', '\164', '\150', + '\040', '\061', '\012', '\115', '\156', '\172', '\040', '\141', + '\156', '\040', '\061', '\012', '\160', '\102', '\164', '\040', + '\164', '\150', '\040', '\061', '\012', '\157', '\141', '\105', + '\040', '\141', '\156', '\040', '\061', '\012', '\163', '\154', + '\113', '\040', '\154', '\145', '\040', '\061', '\012', '\127', + '\154', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\152', '\150', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\166', '\130', '\040', '\166', '\141', '\040', + '\061', '\012', '\106', '\146', '\170', '\040', '\146', '\157', + '\040', '\061', '\012', '\147', '\130', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\143', '\127', '\146', '\040', + '\143', '\150', '\040', '\061', '\012', '\107', '\160', '\171', + '\040', '\160', '\162', '\040', '\061', '\012', '\170', '\155', + '\123', '\040', '\155', '\145', '\040', '\061', '\012', '\147', + '\132', '\156', '\040', '\141', '\156', '\040', '\061', '\012', + '\144', '\152', '\130', '\040', '\144', '\145', '\040', '\061', + '\012', '\142', '\153', '\130', '\040', '\153', '\141', '\040', + '\061', '\012', '\170', '\154', '\120', '\040', '\154', '\145', + '\040', '\061', '\012', '\150', '\103', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\131', '\150', '\152', '\040', + '\164', '\150', '\040', '\061', '\012', '\147', '\167', '\121', + '\040', '\156', '\147', '\040', '\061', '\012', '\153', '\154', + '\104', '\040', '\154', '\145', '\040', '\061', '\012', '\122', + '\150', '\161', '\040', '\164', '\150', '\040', '\061', '\012', + '\141', '\105', '\152', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\160', '\131', '\040', '\151', '\152', '\040', + '\061', '\012', '\160', '\126', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\156', '\112', '\170', '\040', '\141', + '\156', '\040', '\061', '\012', '\172', '\144', '\126', '\040', + '\144', '\145', '\040', '\061', '\012', '\122', '\166', '\146', + '\040', '\166', '\141', '\040', '\061', '\012', '\117', '\161', + '\171', '\040', '\161', '\165', '\040', '\061', '\012', '\172', + '\160', '\124', '\040', '\163', '\172', '\040', '\061', '\012', + '\120', '\172', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\124', '\155', '\040', '\161', '\165', '\040', + '\061', '\012', '\152', '\146', '\161', '\040', '\151', '\152', + '\040', '\061', '\012', '\172', '\164', '\131', '\040', '\164', + '\150', '\040', '\061', '\012', '\132', '\161', '\166', '\040', + '\161', '\165', '\040', '\061', '\012', '\156', '\132', '\142', + '\040', '\141', '\156', '\040', '\061', '\012', '\160', '\110', + '\154', '\040', '\154', '\145', '\040', '\061', '\012', '\121', + '\143', '\162', '\040', '\143', '\150', '\040', '\061', '\012', + '\172', '\126', '\155', '\040', '\163', '\172', '\040', '\061', + '\012', '\160', '\116', '\155', '\040', '\155', '\145', '\040', + '\061', '\012', '\130', '\150', '\152', '\040', '\164', '\150', + '\040', '\061', '\012', '\157', '\131', '\171', '\040', '\157', + '\156', '\040', '\061', '\012', '\106', '\154', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\154', '\167', '\152', + '\040', '\154', '\145', '\040', '\061', '\012', '\162', '\167', + '\110', '\040', '\145', '\162', '\040', '\061', '\012', '\157', + '\127', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\102', '\167', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\152', '\130', '\163', '\040', '\163', '\172', '\040', + '\061', '\012', '\114', '\153', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\154', '\126', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\152', '\130', '\141', '\040', + '\141', '\156', '\040', '\061', '\012', '\150', '\153', '\102', + '\040', '\164', '\150', '\040', '\061', '\012', '\161', '\162', + '\121', '\040', '\161', '\165', '\040', '\061', '\012', '\144', + '\161', '\113', '\040', '\161', '\165', '\040', '\061', '\012', + '\132', '\170', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\147', '\132', '\040', '\156', '\147', '\040', + '\061', '\012', '\106', '\147', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\156', '\167', '\115', '\040', '\141', + '\156', '\040', '\061', '\012', '\127', '\172', '\170', '\040', + '\163', '\172', '\040', '\061', '\012', '\161', '\147', '\142', + '\040', '\156', '\147', '\040', '\061', '\012', '\131', '\147', + '\166', '\040', '\156', '\147', '\040', '\061', '\012', '\130', + '\144', '\144', '\040', '\144', '\145', '\040', '\061', '\012', + '\170', '\152', '\115', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\110', '\142', '\040', '\161', '\165', '\040', + '\061', '\012', '\172', '\113', '\172', '\040', '\163', '\172', + '\040', '\061', '\012', '\144', '\166', '\115', '\040', '\144', + '\145', '\040', '\061', '\012', '\132', '\160', '\170', '\040', + '\160', '\162', '\040', '\061', '\012', '\167', '\120', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\161', '\151', + '\101', '\040', '\151', '\156', '\040', '\061', '\012', '\152', + '\171', '\126', '\040', '\151', '\152', '\040', '\061', '\012', + '\152', '\171', '\122', '\040', '\151', '\152', '\040', '\061', + '\012', '\125', '\157', '\170', '\040', '\157', '\156', '\040', + '\061', '\012', '\121', '\153', '\172', '\040', '\153', '\141', + '\040', '\061', '\012', '\114', '\170', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\146', '\160', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\130', '\155', '\146', + '\040', '\155', '\145', '\040', '\061', '\012', '\153', '\122', + '\170', '\040', '\153', '\141', '\040', '\061', '\012', '\152', + '\106', '\153', '\040', '\151', '\152', '\040', '\061', '\012', + '\156', '\132', '\143', '\040', '\141', '\156', '\040', '\061', + '\012', '\150', '\103', '\160', '\040', '\164', '\150', '\040', + '\061', '\012', '\110', '\142', '\167', '\040', '\167', '\141', + '\040', '\061', '\012', '\172', '\154', '\106', '\040', '\154', + '\145', '\040', '\061', '\012', '\153', '\161', '\111', '\040', + '\161', '\165', '\040', '\061', '\012', '\167', '\127', '\152', + '\040', '\151', '\152', '\040', '\061', '\012', '\161', '\113', + '\153', '\040', '\161', '\165', '\040', '\061', '\012', '\112', + '\160', '\146', '\040', '\160', '\162', '\040', '\061', '\012', + '\154', '\142', '\122', '\040', '\154', '\145', '\040', '\061', + '\012', '\162', '\142', '\112', '\040', '\145', '\162', '\040', + '\061', '\012', '\172', '\146', '\113', '\040', '\163', '\172', + '\040', '\061', '\012', '\147', '\126', '\153', '\040', '\156', + '\147', '\040', '\061', '\012', '\142', '\132', '\170', '\040', + '\142', '\145', '\040', '\061', '\012', '\172', '\156', '\121', + '\040', '\141', '\156', '\040', '\061', '\012', '\147', '\132', + '\142', '\040', '\147', '\141', '\040', '\061', '\012', '\167', + '\164', '\111', '\040', '\164', '\150', '\040', '\061', '\012', + '\142', '\166', '\127', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\150', '\107', '\040', '\164', '\150', '\040', + '\061', '\012', '\170', '\162', '\126', '\040', '\145', '\162', + '\040', '\061', '\012', '\160', '\131', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\142', '\121', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\161', '\160', '\126', + '\040', '\161', '\165', '\040', '\061', '\012', '\160', '\106', + '\155', '\040', '\155', '\145', '\040', '\061', '\012', '\172', + '\144', '\117', '\040', '\144', '\145', '\040', '\061', '\012', + '\112', '\166', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\155', '\121', '\154', '\040', '\154', '\145', '\040', + '\061', '\012', '\170', '\127', '\155', '\040', '\155', '\145', + '\040', '\061', '\012', '\104', '\164', '\172', '\040', '\164', + '\150', '\040', '\061', '\012', '\154', '\113', '\172', '\040', + '\154', '\145', '\040', '\061', '\012', '\144', '\153', '\111', + '\040', '\144', '\145', '\040', '\061', '\012', '\146', '\123', + '\170', '\040', '\146', '\157', '\040', '\061', '\012', '\171', + '\103', '\160', '\040', '\160', '\162', '\040', '\061', '\012', + '\167', '\150', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\126', '\155', '\040', '\154', '\145', '\040', + '\061', '\012', '\171', '\110', '\166', '\040', '\166', '\141', + '\040', '\061', '\012', '\120', '\154', '\155', '\040', '\154', + '\145', '\040', '\061', '\012', '\112', '\160', '\155', '\040', + '\155', '\145', '\040', '\061', '\012', '\150', '\105', '\167', + '\040', '\150', '\141', '\040', '\061', '\012', '\172', '\110', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\165', + '\111', '\152', '\040', '\161', '\165', '\040', '\061', '\012', + '\147', '\172', '\102', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\163', '\126', '\040', '\161', '\165', '\040', + '\061', '\012', '\160', '\142', '\130', '\040', '\160', '\162', + '\040', '\061', '\012', '\152', '\171', '\131', '\040', '\151', + '\152', '\040', '\061', '\012', '\155', '\152', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\172', '\104', '\144', + '\040', '\144', '\145', '\040', '\061', '\012', '\124', '\161', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\146', + '\124', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\161', '\142', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\103', '\152', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\160', '\143', '\127', '\040', '\143', '\150', + '\040', '\061', '\012', '\130', '\150', '\160', '\040', '\164', + '\150', '\040', '\061', '\012', '\146', '\167', '\122', '\040', + '\167', '\141', '\040', '\061', '\012', '\144', '\121', '\155', + '\040', '\144', '\145', '\040', '\061', '\012', '\170', '\103', + '\153', '\040', '\153', '\141', '\040', '\061', '\012', '\171', + '\150', '\115', '\040', '\164', '\150', '\040', '\061', '\012', + '\147', '\154', '\121', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\126', '\142', '\040', '\156', '\147', '\040', + '\061', '\012', '\120', '\144', '\171', '\040', '\144', '\145', + '\040', '\061', '\012', '\171', '\117', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\152', '\132', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\157', '\161', '\132', + '\040', '\161', '\165', '\040', '\061', '\012', '\142', '\161', + '\111', '\040', '\161', '\165', '\040', '\061', '\012', '\152', + '\153', '\130', '\040', '\151', '\152', '\040', '\061', '\012', + '\113', '\146', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\160', '\121', '\040', '\160', '\162', '\040', + '\061', '\012', '\162', '\150', '\130', '\040', '\164', '\150', + '\040', '\061', '\012', '\167', '\152', '\111', '\040', '\151', + '\152', '\040', '\061', '\012', '\102', '\161', '\146', '\040', + '\161', '\165', '\040', '\061', '\012', '\141', '\103', '\160', + '\040', '\141', '\156', '\040', '\061', '\012', '\143', '\143', + '\130', '\040', '\143', '\150', '\040', '\061', '\012', '\166', + '\107', '\155', '\040', '\155', '\141', '\040', '\061', '\012', + '\160', '\141', '\125', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\125', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\147', '\114', '\144', '\040', '\156', '\147', + '\040', '\061', '\012', '\164', '\146', '\112', '\040', '\164', + '\150', '\040', '\061', '\012', '\146', '\167', '\110', '\040', + '\167', '\141', '\040', '\061', '\012', '\120', '\156', '\161', + '\040', '\141', '\156', '\040', '\061', '\012', '\153', '\170', + '\126', '\040', '\153', '\141', '\040', '\061', '\012', '\116', + '\142', '\153', '\040', '\153', '\141', '\040', '\061', '\012', + '\163', '\161', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\103', '\152', '\160', '\040', '\151', '\152', '\040', + '\061', '\012', '\153', '\143', '\132', '\040', '\153', '\141', + '\040', '\061', '\012', '\127', '\161', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\164', '\172', '\131', '\040', + '\164', '\150', '\040', '\061', '\012', '\156', '\161', '\130', + '\040', '\141', '\156', '\040', '\061', '\012', '\131', '\171', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\114', + '\172', '\144', '\040', '\144', '\145', '\040', '\061', '\012', + '\170', '\132', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\163', '\144', '\131', '\040', '\144', '\145', '\040', + '\061', '\012', '\152', '\130', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\116', '\142', '\155', '\040', '\155', + '\145', '\040', '\061', '\012', '\167', '\114', '\162', '\040', + '\145', '\162', '\040', '\061', '\012', '\116', '\161', '\162', + '\040', '\161', '\165', '\040', '\061', '\012', '\132', '\167', + '\170', '\040', '\167', '\141', '\040', '\061', '\012', '\171', + '\166', '\110', '\040', '\166', '\141', '\040', '\061', '\012', + '\171', '\154', '\103', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\171', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\112', '\156', '\172', '\040', '\141', '\156', + '\040', '\061', '\012', '\150', '\110', '\166', '\040', '\164', + '\150', '\040', '\061', '\012', '\172', '\125', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\170', '\147', '\111', + '\040', '\156', '\147', '\040', '\061', '\012', '\132', '\164', + '\160', '\040', '\164', '\150', '\040', '\061', '\012', '\126', + '\166', '\142', '\040', '\166', '\141', '\040', '\061', '\012', + '\164', '\107', '\156', '\040', '\164', '\150', '\040', '\061', + '\012', '\125', '\152', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\152', '\110', '\163', '\040', '\163', '\172', + '\040', '\061', '\012', '\142', '\127', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\142', '\130', '\162', '\040', + '\145', '\162', '\040', '\061', '\012', '\150', '\106', '\147', + '\040', '\164', '\150', '\040', '\061', '\012', '\147', '\144', + '\124', '\040', '\156', '\147', '\040', '\061', '\012', '\161', + '\110', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\154', '\103', '\152', '\040', '\154', '\145', '\040', '\061', + '\012', '\155', '\126', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\160', '\121', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\166', '\127', '\154', '\040', '\154', + '\145', '\040', '\061', '\012', '\171', '\106', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\144', '\152', '\131', + '\040', '\144', '\145', '\040', '\061', '\012', '\142', '\164', + '\121', '\040', '\164', '\150', '\040', '\061', '\012', '\166', + '\154', '\115', '\040', '\154', '\145', '\040', '\061', '\012', + '\111', '\167', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\120', '\144', '\142', '\040', '\144', '\145', '\040', + '\061', '\012', '\152', '\164', '\121', '\040', '\164', '\150', + '\040', '\061', '\012', '\170', '\152', '\122', '\040', '\151', + '\152', '\040', '\061', '\012', '\144', '\150', '\127', '\040', + '\164', '\150', '\040', '\061', '\012', '\172', '\130', '\163', + '\040', '\163', '\172', '\040', '\061', '\012', '\146', '\142', + '\105', '\040', '\142', '\145', '\040', '\061', '\012', '\110', + '\161', '\162', '\040', '\161', '\165', '\040', '\061', '\012', + '\166', '\114', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\142', '\104', '\040', '\153', '\141', '\040', + '\061', '\012', '\166', '\125', '\144', '\040', '\144', '\145', + '\040', '\061', '\012', '\171', '\132', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\121', '\153', '\145', '\040', + '\154', '\145', '\040', '\061', '\012', '\146', '\150', '\107', + '\040', '\164', '\150', '\040', '\061', '\012', '\145', '\110', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\166', + '\110', '\152', '\040', '\151', '\152', '\040', '\061', '\012', + '\124', '\146', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\165', '\157', '\101', '\040', '\161', '\165', '\040', + '\061', '\012', '\172', '\103', '\170', '\040', '\163', '\172', + '\040', '\061', '\012', '\172', '\114', '\153', '\040', '\163', + '\172', '\040', '\061', '\012', '\152', '\144', '\127', '\040', + '\144', '\145', '\040', '\061', '\012', '\103', '\147', '\156', + '\040', '\141', '\156', '\040', '\061', '\012', '\114', '\162', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\171', + '\117', '\151', '\040', '\151', '\156', '\040', '\061', '\012', + '\161', '\117', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\161', '\163', '\040', '\161', '\165', '\040', + '\061', '\012', '\154', '\164', '\121', '\040', '\164', '\150', + '\040', '\061', '\012', '\156', '\167', '\125', '\040', '\141', + '\156', '\040', '\061', '\012', '\172', '\131', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\107', '\172', '\163', + '\040', '\163', '\164', '\040', '\061', '\012', '\156', '\127', + '\166', '\040', '\141', '\156', '\040', '\061', '\012', '\154', + '\116', '\170', '\040', '\154', '\145', '\040', '\061', '\012', + '\127', '\161', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\143', '\104', '\040', '\143', '\150', '\040', + '\061', '\012', '\166', '\146', '\104', '\040', '\166', '\141', + '\040', '\061', '\012', '\161', '\126', '\144', '\040', '\161', + '\165', '\040', '\061', '\012', '\127', '\172', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\152', '\146', '\110', + '\040', '\151', '\152', '\040', '\061', '\012', '\122', '\162', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\161', + '\104', '\162', '\040', '\161', '\165', '\040', '\061', '\012', + '\154', '\117', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\167', '\132', '\040', '\167', '\141', '\040', + '\061', '\012', '\155', '\121', '\167', '\040', '\155', '\145', + '\040', '\061', '\012', '\156', '\161', '\113', '\040', '\141', + '\156', '\040', '\061', '\012', '\125', '\166', '\154', '\040', + '\154', '\145', '\040', '\061', '\012', '\153', '\122', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\126', '\150', + '\147', '\040', '\164', '\150', '\040', '\061', '\012', '\170', + '\163', '\104', '\040', '\163', '\164', '\040', '\061', '\012', + '\114', '\144', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\163', '\121', '\166', '\040', '\163', '\164', '\040', + '\061', '\012', '\161', '\115', '\152', '\040', '\161', '\165', + '\040', '\061', '\012', '\150', '\142', '\121', '\040', '\164', + '\150', '\040', '\061', '\012', '\143', '\152', '\130', '\040', + '\143', '\150', '\040', '\061', '\012', '\156', '\142', '\124', + '\040', '\141', '\156', '\040', '\061', '\012', '\170', '\116', + '\146', '\040', '\146', '\157', '\040', '\061', '\012', '\167', + '\103', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\152', '\156', '\130', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\132', '\146', '\040', '\164', '\150', '\040', + '\061', '\012', '\161', '\103', '\153', '\040', '\161', '\165', + '\040', '\061', '\012', '\144', '\110', '\153', '\040', '\144', + '\145', '\040', '\061', '\012', '\103', '\143', '\161', '\040', + '\143', '\150', '\040', '\061', '\012', '\165', '\115', '\146', + '\040', '\161', '\165', '\040', '\061', '\012', '\142', '\166', + '\107', '\040', '\166', '\141', '\040', '\061', '\012', '\172', + '\120', '\172', '\040', '\163', '\172', '\040', '\061', '\012', + '\171', '\111', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\154', '\110', '\170', '\040', '\154', '\145', '\040', + '\061', '\012', '\146', '\156', '\102', '\040', '\141', '\156', + '\040', '\061', '\012', '\105', '\142', '\170', '\040', '\142', + '\145', '\040', '\061', '\012', '\162', '\107', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\155', '\147', '\104', + '\040', '\156', '\147', '\040', '\061', '\012', '\150', '\112', + '\147', '\040', '\164', '\150', '\040', '\061', '\012', '\152', + '\143', '\107', '\040', '\143', '\150', '\040', '\061', '\012', + '\131', '\142', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\157', '\104', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\152', '\122', '\170', '\040', '\151', '\152', + '\040', '\061', '\012', '\153', '\112', '\146', '\040', '\153', + '\141', '\040', '\061', '\012', '\164', '\106', '\166', '\040', + '\164', '\150', '\040', '\061', '\012', '\107', '\144', '\166', + '\040', '\144', '\145', '\040', '\061', '\012', '\146', '\110', + '\156', '\040', '\141', '\156', '\040', '\061', '\012', '\125', + '\161', '\160', '\040', '\161', '\165', '\040', '\061', '\012', + '\143', '\131', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\110', '\160', '\040', '\153', '\141', '\040', + '\061', '\012', '\161', '\150', '\132', '\040', '\164', '\150', + '\040', '\061', '\012', '\167', '\132', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\153', '\121', '\164', '\040', + '\164', '\150', '\040', '\061', '\012', '\150', '\167', '\110', + '\040', '\164', '\150', '\040', '\061', '\012', '\170', '\172', + '\125', '\040', '\163', '\172', '\040', '\061', '\012', '\164', + '\121', '\147', '\040', '\164', '\150', '\040', '\061', '\012', + '\121', '\142', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\126', '\154', '\040', '\154', '\145', '\040', + '\061', '\012', '\161', '\112', '\144', '\040', '\161', '\165', + '\040', '\061', '\012', '\130', '\162', '\146', '\040', '\145', + '\162', '\040', '\061', '\012', '\146', '\115', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\161', '\112', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\104', '\161', + '\171', '\040', '\161', '\165', '\040', '\061', '\012', '\161', + '\115', '\163', '\040', '\161', '\165', '\040', '\061', '\012', + '\146', '\172', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\127', '\144', '\170', '\040', '\144', '\145', '\040', + '\061', '\012', '\124', '\144', '\167', '\040', '\167', '\141', + '\040', '\061', '\012', '\155', '\143', '\124', '\040', '\143', + '\150', '\040', '\061', '\012', '\146', '\117', '\144', '\040', + '\144', '\145', '\040', '\061', '\012', '\113', '\147', '\152', + '\040', '\156', '\147', '\040', '\061', '\012', '\171', '\162', + '\124', '\040', '\145', '\162', '\040', '\061', '\012', '\142', + '\161', '\101', '\040', '\161', '\165', '\040', '\061', '\012', + '\163', '\156', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\114', '\172', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\147', '\114', '\167', '\040', '\156', '\147', + '\040', '\061', '\012', '\144', '\114', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\121', '\172', '\162', '\040', + '\145', '\162', '\040', '\061', '\012', '\121', '\162', '\156', + '\040', '\141', '\156', '\040', '\061', '\012', '\145', '\106', + '\156', '\040', '\141', '\156', '\040', '\061', '\012', '\116', + '\155', '\167', '\040', '\167', '\141', '\040', '\061', '\012', + '\160', '\170', '\105', '\040', '\160', '\162', '\040', '\061', + '\012', '\103', '\161', '\153', '\040', '\161', '\165', '\040', + '\061', '\012', '\127', '\143', '\144', '\040', '\143', '\150', + '\040', '\061', '\012', '\146', '\130', '\167', '\040', '\167', + '\141', '\040', '\061', '\012', '\146', '\142', '\125', '\040', + '\142', '\145', '\040', '\061', '\012', '\141', '\145', '\117', + '\040', '\141', '\156', '\040', '\061', '\012', '\163', '\166', + '\126', '\040', '\163', '\164', '\040', '\061', '\012', '\171', + '\126', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\163', '\122', '\160', '\040', '\163', '\164', '\040', '\061', + '\012', '\162', '\170', '\125', '\040', '\145', '\162', '\040', + '\061', '\012', '\161', '\150', '\113', '\040', '\164', '\150', + '\040', '\061', '\012', '\165', '\121', '\167', '\040', '\161', + '\165', '\040', '\061', '\012', '\157', '\130', '\167', '\040', + '\157', '\156', '\040', '\061', '\012', '\112', '\166', '\167', + '\040', '\166', '\141', '\040', '\061', '\012', '\153', '\166', + '\110', '\040', '\153', '\141', '\040', '\061', '\012', '\172', + '\126', '\171', '\040', '\163', '\172', '\040', '\061', '\012', + '\162', '\117', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\127', '\170', '\040', '\143', '\150', '\040', + '\061', '\012', '\151', '\130', '\166', '\040', '\151', '\156', + '\040', '\061', '\012', '\143', '\102', '\153', '\040', '\143', + '\150', '\040', '\061', '\012', '\170', '\153', '\115', '\040', + '\153', '\141', '\040', '\061', '\012', '\166', '\110', '\142', + '\040', '\166', '\141', '\040', '\061', '\012', '\152', '\142', + '\127', '\040', '\151', '\152', '\040', '\061', '\012', '\155', + '\131', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\146', '\156', '\110', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\122', '\152', '\040', '\163', '\172', '\040', + '\061', '\012', '\150', '\166', '\116', '\040', '\164', '\150', + '\040', '\061', '\012', '\157', '\115', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\171', '\161', '\117', '\040', + '\161', '\165', '\040', '\061', '\012', '\146', '\102', '\146', + '\040', '\146', '\157', '\040', '\061', '\012', '\157', '\120', + '\152', '\040', '\157', '\156', '\040', '\061', '\012', '\146', + '\106', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\154', '\126', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\164', '\112', '\040', '\164', '\150', '\040', + '\061', '\012', '\116', '\164', '\152', '\040', '\164', '\150', + '\040', '\061', '\012', '\162', '\167', '\114', '\040', '\145', + '\162', '\040', '\061', '\012', '\143', '\106', '\172', '\040', + '\143', '\150', '\040', '\061', '\012', '\152', '\126', '\144', + '\040', '\144', '\145', '\040', '\061', '\012', '\107', '\142', + '\166', '\040', '\166', '\141', '\040', '\061', '\012', '\157', + '\112', '\156', '\040', '\141', '\156', '\040', '\061', '\012', + '\167', '\153', '\114', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\157', '\124', '\040', '\161', '\165', '\040', + '\061', '\012', '\121', '\170', '\153', '\040', '\153', '\141', + '\040', '\061', '\012', '\162', '\132', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\103', '\147', '\144', '\040', + '\156', '\147', '\040', '\061', '\012', '\147', '\166', '\127', + '\040', '\156', '\147', '\040', '\061', '\012', '\153', '\131', + '\166', '\040', '\153', '\141', '\040', '\061', '\012', '\161', + '\152', '\122', '\040', '\161', '\165', '\040', '\061', '\012', + '\126', '\156', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\112', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\170', '\127', '\171', '\040', '\156', '\171', + '\040', '\061', '\012', '\142', '\130', '\154', '\040', '\154', + '\145', '\040', '\061', '\012', '\170', '\126', '\153', '\040', + '\153', '\141', '\040', '\061', '\012', '\170', '\165', '\107', + '\040', '\161', '\165', '\040', '\061', '\012', '\110', '\172', + '\163', '\040', '\163', '\164', '\040', '\061', '\012', '\165', + '\104', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\131', '\167', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\112', '\153', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\107', '\144', '\155', '\040', '\144', '\145', + '\040', '\061', '\012', '\161', '\143', '\117', '\040', '\143', + '\150', '\040', '\061', '\012', '\150', '\154', '\110', '\040', + '\164', '\150', '\040', '\061', '\012', '\112', '\146', '\166', + '\040', '\166', '\141', '\040', '\061', '\012', '\143', '\114', + '\156', '\040', '\141', '\156', '\040', '\061', '\012', '\167', + '\172', '\107', '\040', '\163', '\172', '\040', '\061', '\012', + '\171', '\150', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\146', '\104', '\040', '\153', '\141', '\040', + '\061', '\012', '\153', '\142', '\112', '\040', '\153', '\141', + '\040', '\061', '\012', '\116', '\161', '\160', '\040', '\161', + '\165', '\040', '\061', '\012', '\147', '\131', '\161', '\040', + '\156', '\147', '\040', '\061', '\012', '\172', '\164', '\115', + '\040', '\164', '\150', '\040', '\061', '\012', '\152', '\143', + '\104', '\040', '\143', '\150', '\040', '\061', '\012', '\167', + '\147', '\131', '\040', '\156', '\147', '\040', '\061', '\012', + '\161', '\144', '\124', '\040', '\144', '\141', '\040', '\061', + '\012', '\166', '\124', '\167', '\040', '\166', '\141', '\040', + '\061', '\012', '\143', '\116', '\172', '\040', '\143', '\150', + '\040', '\061', '\012', '\112', '\142', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\130', '\143', '\152', '\040', + '\143', '\150', '\040', '\061', '\012', '\162', '\125', '\167', + '\040', '\145', '\162', '\040', '\061', '\012', '\147', '\130', + '\166', '\040', '\156', '\147', '\040', '\061', '\012', '\144', + '\122', '\146', '\040', '\144', '\145', '\040', '\061', '\012', + '\142', '\112', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\141', '\161', '\101', '\040', '\141', '\156', '\040', + '\061', '\012', '\165', '\117', '\172', '\040', '\161', '\165', + '\040', '\061', '\012', '\167', '\120', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\165', '\104', '\167', '\040', + '\161', '\165', '\040', '\061', '\012', '\155', '\161', '\106', + '\040', '\161', '\165', '\040', '\061', '\012', '\143', '\130', + '\162', '\040', '\143', '\150', '\040', '\061', '\012', '\171', + '\162', '\114', '\040', '\145', '\162', '\040', '\061', '\012', + '\156', '\112', '\153', '\040', '\141', '\156', '\040', '\061', + '\012', '\150', '\163', '\131', '\040', '\164', '\150', '\040', + '\061', '\012', '\132', '\161', '\163', '\040', '\161', '\165', + '\040', '\061', '\012', '\161', '\145', '\123', '\040', '\161', + '\165', '\040', '\061', '\012', '\142', '\114', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\152', '\105', '\157', + '\040', '\157', '\156', '\040', '\061', '\012', '\160', '\155', + '\105', '\040', '\155', '\145', '\040', '\061', '\012', '\152', + '\111', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\166', '\172', '\132', '\040', '\163', '\172', '\040', '\061', + '\012', '\121', '\150', '\144', '\040', '\164', '\150', '\040', + '\061', '\012', '\143', '\156', '\116', '\040', '\141', '\156', + '\040', '\061', '\012', '\142', '\120', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\160', '\132', '\167', '\040', + '\160', '\162', '\040', '\061', '\012', '\151', '\167', '\122', + '\040', '\151', '\156', '\040', '\061', '\012', '\157', '\112', + '\166', '\040', '\153', '\157', '\040', '\061', '\012', '\165', + '\146', '\111', '\040', '\161', '\165', '\040', '\061', '\012', + '\167', '\113', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\165', '\127', '\166', '\040', '\161', '\165', '\040', + '\061', '\012', '\146', '\103', '\146', '\040', '\146', '\157', + '\040', '\061', '\012', '\167', '\102', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\125', '\171', '\146', '\040', + '\156', '\171', '\040', '\061', '\012', '\165', '\126', '\170', + '\040', '\161', '\165', '\040', '\061', '\012', '\153', '\113', + '\146', '\040', '\153', '\141', '\040', '\061', '\012', '\155', + '\162', '\132', '\040', '\145', '\162', '\040', '\061', '\012', + '\154', '\130', '\142', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\112', '\155', '\040', '\163', '\172', '\040', + '\061', '\012', '\167', '\131', '\162', '\040', '\145', '\162', + '\040', '\061', '\012', '\110', '\153', '\167', '\040', '\153', + '\141', '\040', '\061', '\012', '\105', '\167', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\170', '\112', '\171', + '\040', '\156', '\171', '\040', '\061', '\012', '\105', '\155', + '\170', '\040', '\155', '\145', '\040', '\061', '\012', '\143', + '\161', '\114', '\040', '\143', '\150', '\040', '\061', '\012', + '\172', '\126', '\153', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\120', '\142', '\040', '\142', '\145', '\040', + '\061', '\012', '\172', '\143', '\103', '\040', '\143', '\150', + '\040', '\061', '\012', '\116', '\144', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\165', '\127', '\146', '\040', + '\161', '\165', '\040', '\061', '\012', '\153', '\143', '\115', + '\040', '\143', '\150', '\040', '\061', '\012', '\164', '\153', + '\102', '\040', '\164', '\150', '\040', '\061', '\012', '\171', + '\150', '\161', '\040', '\164', '\150', '\040', '\061', '\012', + '\161', '\141', '\120', '\040', '\141', '\156', '\040', '\061', + '\012', '\162', '\126', '\163', '\040', '\145', '\162', '\040', + '\061', '\012', '\144', '\114', '\144', '\040', '\144', '\145', + '\040', '\061', '\012', '\123', '\147', '\155', '\040', '\156', + '\147', '\040', '\061', '\012', '\130', '\150', '\170', '\040', + '\164', '\150', '\040', '\061', '\012', '\170', '\161', '\110', + '\040', '\161', '\165', '\040', '\061', '\012', '\113', '\161', + '\171', '\040', '\161', '\165', '\040', '\061', '\012', '\171', + '\122', '\167', '\040', '\167', '\141', '\040', '\061', '\012', + '\127', '\144', '\167', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\143', '\121', '\040', '\143', '\150', '\040', + '\061', '\012', '\172', '\142', '\160', '\040', '\163', '\172', + '\040', '\061', '\012', '\144', '\164', '\131', '\040', '\164', + '\150', '\040', '\061', '\012', '\143', '\167', '\102', '\040', + '\143', '\150', '\040', '\061', '\012', '\156', '\146', '\126', + '\040', '\141', '\156', '\040', '\061', '\012', '\143', '\147', + '\120', '\040', '\143', '\150', '\040', '\061', '\012', '\160', + '\167', '\127', '\040', '\160', '\162', '\040', '\061', '\012', + '\160', '\161', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\153', '\160', '\040', '\153', '\141', '\040', + '\061', '\012', '\151', '\172', '\112', '\040', '\151', '\156', + '\040', '\061', '\012', '\143', '\131', '\167', '\040', '\143', + '\150', '\040', '\061', '\012', '\151', '\121', '\154', '\040', + '\151', '\156', '\040', '\061', '\012', '\121', '\166', '\171', + '\040', '\166', '\141', '\040', '\061', '\012', '\171', '\154', + '\122', '\040', '\154', '\145', '\040', '\061', '\012', '\163', + '\106', '\160', '\040', '\163', '\164', '\040', '\061', '\012', + '\114', '\161', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\156', '\120', '\040', '\141', '\156', '\040', + '\061', '\012', '\147', '\131', '\154', '\040', '\156', '\147', + '\040', '\061', '\012', '\167', '\111', '\162', '\040', '\145', + '\162', '\040', '\061', '\012', '\146', '\161', '\122', '\040', + '\161', '\165', '\040', '\061', '\012', '\121', '\160', '\153', + '\040', '\153', '\141', '\040', '\061', '\012', '\161', '\130', + '\172', '\040', '\161', '\165', '\040', '\061', '\012', '\114', + '\162', '\162', '\040', '\145', '\162', '\040', '\061', '\012', + '\163', '\152', '\111', '\040', '\163', '\164', '\040', '\061', + '\012', '\151', '\171', '\130', '\040', '\151', '\156', '\040', + '\061', '\012', '\132', '\146', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\166', '\164', '\110', '\040', '\164', + '\150', '\040', '\061', '\012', '\143', '\132', '\146', '\040', + '\143', '\150', '\040', '\061', '\012', '\150', '\130', '\160', + '\040', '\164', '\150', '\040', '\061', '\012', '\162', '\112', + '\167', '\040', '\145', '\162', '\040', '\061', '\012', '\147', + '\142', '\120', '\040', '\156', '\147', '\040', '\061', '\012', + '\121', '\165', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\122', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\154', '\130', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\160', '\126', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\153', '\107', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\116', '\170', '\162', + '\040', '\145', '\162', '\040', '\061', '\012', '\171', '\113', + '\153', '\040', '\153', '\141', '\040', '\061', '\012', '\170', + '\101', '\157', '\040', '\157', '\156', '\040', '\061', '\012', + '\157', '\125', '\170', '\040', '\157', '\156', '\040', '\061', + '\012', '\156', '\127', '\170', '\040', '\141', '\156', '\040', + '\061', '\012', '\146', '\167', '\125', '\040', '\167', '\141', + '\040', '\061', '\012', '\155', '\113', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\161', '\150', '\117', '\040', + '\164', '\150', '\040', '\061', '\012', '\163', '\107', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\127', '\167', + '\165', '\040', '\161', '\165', '\040', '\061', '\012', '\143', + '\156', '\105', '\040', '\141', '\156', '\040', '\061', '\012', + '\164', '\152', '\123', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\171', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\171', '\127', '\155', '\040', '\155', '\145', + '\040', '\061', '\012', '\121', '\144', '\152', '\040', '\144', + '\145', '\040', '\061', '\012', '\152', '\123', '\144', '\040', + '\144', '\145', '\040', '\061', '\012', '\111', '\157', '\171', + '\040', '\157', '\156', '\040', '\061', '\012', '\130', '\160', + '\160', '\040', '\160', '\162', '\040', '\061', '\012', '\170', + '\112', '\142', '\040', '\142', '\145', '\040', '\061', '\012', + '\170', '\166', '\124', '\040', '\166', '\141', '\040', '\061', + '\012', '\143', '\144', '\124', '\040', '\143', '\150', '\040', + '\061', '\012', '\153', '\150', '\130', '\040', '\164', '\150', + '\040', '\061', '\012', '\150', '\126', '\160', '\040', '\164', + '\150', '\040', '\061', '\012', '\143', '\152', '\124', '\040', + '\143', '\150', '\040', '\061', '\012', '\110', '\161', '\146', + '\040', '\161', '\165', '\040', '\061', '\012', '\156', '\142', + '\120', '\040', '\141', '\156', '\040', '\061', '\012', '\125', + '\167', '\142', '\040', '\167', '\141', '\040', '\061', '\012', + '\113', '\143', '\142', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\163', '\121', '\040', '\161', '\165', '\040', + '\061', '\012', '\164', '\153', '\132', '\040', '\164', '\150', + '\040', '\061', '\012', '\172', '\162', '\130', '\040', '\145', + '\162', '\040', '\061', '\012', '\172', '\142', '\116', '\040', + '\163', '\172', '\040', '\061', '\012', '\155', '\131', '\151', + '\040', '\151', '\156', '\040', '\061', '\012', '\147', '\114', + '\170', '\040', '\156', '\147', '\040', '\061', '\012', '\163', + '\107', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\120', '\142', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\147', '\143', '\126', '\040', '\143', '\150', '\040', + '\061', '\012', '\121', '\152', '\146', '\040', '\151', '\152', + '\040', '\061', '\012', '\167', '\166', '\102', '\040', '\166', + '\141', '\040', '\061', '\012', '\147', '\113', '\160', '\040', + '\156', '\147', '\040', '\061', '\012', '\152', '\132', '\171', + '\040', '\151', '\152', '\040', '\061', '\012', '\161', '\150', + '\127', '\040', '\164', '\150', '\040', '\061', '\012', '\166', + '\103', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\114', '\162', '\153', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\122', '\167', '\040', '\167', '\141', '\040', + '\061', '\012', '\143', '\115', '\152', '\040', '\143', '\150', + '\040', '\061', '\012', '\157', '\150', '\113', '\040', '\164', + '\150', '\040', '\061', '\012', '\146', '\162', '\113', '\040', + '\145', '\162', '\040', '\061', '\012', '\144', '\121', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\110', '\144', + '\152', '\040', '\144', '\145', '\040', '\061', '\012', '\102', + '\153', '\170', '\040', '\153', '\141', '\040', '\061', '\012', + '\171', '\130', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\146', '\144', '\117', '\040', '\144', '\145', '\040', + '\061', '\012', '\163', '\127', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\130', '\164', '\146', '\040', '\164', + '\150', '\040', '\061', '\012', '\162', '\125', '\170', '\040', + '\141', '\162', '\040', '\061', '\012', '\161', '\110', '\155', + '\040', '\161', '\165', '\040', '\061', '\012', '\153', '\121', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\167', + '\172', '\125', '\040', '\163', '\172', '\040', '\061', '\012', + '\166', '\124', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\153', '\116', '\040', '\163', '\172', '\040', + '\061', '\012', '\106', '\161', '\160', '\040', '\161', '\165', + '\040', '\061', '\012', '\170', '\112', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\167', '\153', '\121', '\040', + '\153', '\141', '\040', '\061', '\012', '\167', '\170', '\106', + '\040', '\167', '\141', '\040', '\061', '\012', '\166', '\122', + '\152', '\040', '\151', '\152', '\040', '\061', '\012', '\152', + '\172', '\104', '\040', '\163', '\172', '\040', '\061', '\012', + '\132', '\161', '\165', '\040', '\165', '\156', '\040', '\061', + '\012', '\172', '\127', '\167', '\040', '\163', '\172', '\040', + '\061', '\012', '\172', '\147', '\125', '\040', '\156', '\147', + '\040', '\061', '\012', '\165', '\147', '\130', '\040', '\156', + '\147', '\040', '\061', '\012', '\160', '\155', '\102', '\040', + '\155', '\145', '\040', '\061', '\012', '\147', '\172', '\101', + '\040', '\156', '\147', '\040', '\061', '\012', '\132', '\152', + '\152', '\040', '\151', '\152', '\040', '\061', '\012', '\170', + '\111', '\152', '\040', '\151', '\152', '\040', '\061', '\012', + '\170', '\157', '\113', '\040', '\157', '\156', '\040', '\061', + '\012', '\107', '\161', '\170', '\040', '\161', '\165', '\040', + '\061', '\012', '\165', '\114', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\154', '\107', '\167', '\040', '\154', + '\145', '\040', '\061', '\012', '\164', '\132', '\161', '\040', + '\164', '\150', '\040', '\061', '\012', '\172', '\143', '\116', + '\040', '\143', '\150', '\040', '\061', '\012', '\171', '\120', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\162', + '\161', '\116', '\040', '\161', '\165', '\040', '\061', '\012', + '\160', '\167', '\107', '\040', '\160', '\162', '\040', '\061', + '\012', '\166', '\146', '\120', '\040', '\166', '\141', '\040', + '\061', '\012', '\166', '\111', '\171', '\040', '\166', '\141', + '\040', '\061', '\012', '\166', '\105', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\152', '\161', '\104', '\040', + '\161', '\165', '\040', '\061', '\012', '\110', '\170', '\165', + '\040', '\161', '\165', '\040', '\061', '\012', '\161', '\114', + '\163', '\040', '\161', '\165', '\040', '\061', '\012', '\112', + '\160', '\171', '\040', '\160', '\162', '\040', '\061', '\012', + '\160', '\122', '\167', '\040', '\160', '\162', '\040', '\061', + '\012', '\146', '\132', '\163', '\040', '\163', '\164', '\040', + '\061', '\012', '\126', '\166', '\170', '\040', '\166', '\141', + '\040', '\061', '\012', '\172', '\153', '\102', '\040', '\163', + '\172', '\040', '\061', '\012', '\171', '\107', '\153', '\040', + '\153', '\141', '\040', '\061', '\012', '\153', '\166', '\132', + '\040', '\153', '\141', '\040', '\061', '\012', '\143', '\161', + '\127', '\040', '\143', '\150', '\040', '\061', '\012', '\167', + '\114', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\131', '\160', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\162', '\122', '\040', '\145', '\162', '\040', + '\061', '\012', '\166', '\167', '\132', '\040', '\166', '\141', + '\040', '\061', '\012', '\147', '\126', '\144', '\040', '\156', + '\147', '\040', '\061', '\012', '\151', '\103', '\167', '\040', + '\151', '\152', '\040', '\061', '\012', '\106', '\170', '\167', + '\040', '\167', '\141', '\040', '\061', '\012', '\161', '\171', + '\132', '\040', '\161', '\165', '\040', '\061', '\012', '\161', + '\147', '\124', '\040', '\161', '\165', '\040', '\061', '\012', + '\170', '\114', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\160', '\130', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\147', '\116', '\166', '\040', '\156', '\147', + '\040', '\061', '\012', '\110', '\147', '\172', '\040', '\156', + '\147', '\040', '\061', '\012', '\172', '\112', '\166', '\040', + '\163', '\172', '\040', '\061', '\012', '\110', '\166', '\155', + '\040', '\166', '\141', '\040', '\061', '\012', '\165', '\130', + '\142', '\040', '\161', '\165', '\040', '\061', '\012', '\154', + '\114', '\172', '\040', '\154', '\145', '\040', '\061', '\012', + '\144', '\167', '\120', '\040', '\144', '\145', '\040', '\061', + '\012', '\147', '\166', '\116', '\040', '\156', '\147', '\040', + '\061', '\012', '\143', '\160', '\106', '\040', '\143', '\150', + '\040', '\061', '\012', '\166', '\132', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\120', '\146', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\170', '\143', '\111', + '\040', '\143', '\150', '\040', '\061', '\012', '\171', '\126', + '\160', '\040', '\160', '\162', '\040', '\061', '\012', '\146', + '\144', '\103', '\040', '\144', '\145', '\040', '\061', '\012', + '\160', '\142', '\105', '\040', '\160', '\162', '\040', '\061', + '\012', '\152', '\121', '\155', '\040', '\151', '\152', '\040', + '\061', '\012', '\124', '\161', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\167', '\115', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\107', '\153', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\164', '\144', '\126', + '\040', '\164', '\150', '\040', '\061', '\012', '\170', '\111', + '\153', '\040', '\153', '\141', '\040', '\061', '\012', '\150', + '\110', '\160', '\040', '\164', '\150', '\040', '\061', '\012', + '\114', '\163', '\142', '\040', '\163', '\164', '\040', '\061', + '\012', '\127', '\166', '\163', '\040', '\163', '\164', '\040', + '\061', '\012', '\121', '\143', '\167', '\040', '\143', '\150', + '\040', '\061', '\012', '\147', '\146', '\121', '\040', '\156', + '\147', '\040', '\061', '\012', '\106', '\152', '\164', '\040', + '\164', '\150', '\040', '\061', '\012', '\170', '\102', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\146', '\114', + '\170', '\040', '\146', '\157', '\040', '\061', '\012', '\172', + '\153', '\122', '\040', '\163', '\172', '\040', '\061', '\012', + '\153', '\152', '\101', '\040', '\151', '\152', '\040', '\061', + '\012', '\106', '\143', '\167', '\040', '\143', '\150', '\040', + '\061', '\012', '\146', '\150', '\124', '\040', '\164', '\150', + '\040', '\061', '\012', '\161', '\151', '\113', '\040', '\161', + '\165', '\040', '\061', '\012', '\167', '\121', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\160', '\130', '\154', + '\040', '\154', '\145', '\040', '\061', '\012', '\150', '\114', + '\147', '\040', '\164', '\150', '\040', '\061', '\012', '\152', + '\112', '\167', '\040', '\151', '\152', '\040', '\061', '\012', + '\163', '\117', '\152', '\040', '\163', '\164', '\040', '\061', + '\012', '\166', '\127', '\142', '\040', '\166', '\141', '\040', + '\061', '\012', '\101', '\152', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\166', '\113', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\151', '\111', '\171', '\040', + '\151', '\156', '\040', '\061', '\012', '\160', '\112', '\171', + '\040', '\160', '\162', '\040', '\061', '\012', '\114', '\161', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\167', + '\102', '\144', '\040', '\144', '\145', '\040', '\061', '\012', + '\153', '\122', '\142', '\040', '\153', '\141', '\040', '\061', + '\012', '\114', '\143', '\160', '\040', '\143', '\150', '\040', + '\061', '\012', '\147', '\146', '\102', '\040', '\156', '\147', + '\040', '\061', '\012', '\172', '\126', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\161', '\127', '\146', '\040', + '\161', '\165', '\040', '\061', '\012', '\121', '\171', '\146', + '\040', '\156', '\171', '\040', '\061', '\012', '\160', '\165', + '\106', '\040', '\161', '\165', '\040', '\061', '\012', '\146', + '\111', '\145', '\040', '\145', '\162', '\040', '\061', '\012', + '\167', '\107', '\142', '\040', '\167', '\141', '\040', '\061', + '\012', '\152', '\152', '\114', '\040', '\151', '\152', '\040', + '\061', '\012', '\150', '\143', '\105', '\040', '\164', '\150', + '\040', '\061', '\012', '\161', '\150', '\160', '\040', '\164', + '\150', '\040', '\061', '\012', '\147', '\170', '\116', '\040', + '\156', '\147', '\040', '\061', '\012', '\164', '\115', '\144', + '\040', '\164', '\150', '\040', '\061', '\012', '\122', '\172', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\143', + '\147', '\117', '\040', '\143', '\150', '\040', '\061', '\012', + '\166', '\155', '\124', '\040', '\166', '\141', '\040', '\061', + '\012', '\104', '\143', '\161', '\040', '\143', '\150', '\040', + '\061', '\012', '\161', '\157', '\111', '\040', '\161', '\165', + '\040', '\061', '\012', '\116', '\161', '\172', '\040', '\161', + '\165', '\040', '\061', '\012', '\166', '\150', '\115', '\040', + '\164', '\150', '\040', '\061', '\012', '\147', '\102', '\161', + '\040', '\156', '\147', '\040', '\061', '\012', '\152', '\127', + '\166', '\040', '\151', '\152', '\040', '\061', '\012', '\170', + '\155', '\105', '\040', '\155', '\145', '\040', '\061', '\012', + '\161', '\143', '\144', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\131', '\152', '\040', '\154', '\145', '\040', + '\061', '\012', '\144', '\104', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\170', '\125', '\141', '\040', '\141', + '\156', '\040', '\061', '\012', '\153', '\126', '\154', '\040', + '\154', '\145', '\040', '\061', '\012', '\167', '\161', '\116', + '\040', '\161', '\165', '\040', '\061', '\012', '\165', '\165', + '\111', '\040', '\161', '\165', '\040', '\061', '\012', '\127', + '\172', '\146', '\040', '\163', '\172', '\040', '\061', '\012', + '\171', '\166', '\130', '\040', '\166', '\141', '\040', '\061', + '\012', '\120', '\171', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\167', '\165', '\125', '\040', '\161', '\165', + '\040', '\061', '\012', '\150', '\114', '\160', '\040', '\164', + '\150', '\040', '\061', '\012', '\161', '\161', '\114', '\040', + '\161', '\165', '\040', '\061', '\012', '\143', '\126', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\106', '\147', + '\163', '\040', '\156', '\147', '\040', '\061', '\012', '\170', + '\152', '\106', '\040', '\151', '\152', '\040', '\061', '\012', + '\167', '\153', '\107', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\112', '\162', '\040', '\161', '\165', '\040', + '\061', '\012', '\107', '\172', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\111', '\170', '\166', '\040', '\166', + '\141', '\040', '\061', '\012', '\150', '\115', '\166', '\040', + '\164', '\150', '\040', '\061', '\012', '\144', '\146', '\121', + '\040', '\144', '\145', '\040', '\061', '\012', '\145', '\117', + '\170', '\040', '\145', '\162', '\040', '\061', '\012', '\155', + '\110', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\132', '\153', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\156', '\161', '\127', '\040', '\141', '\156', '\040', + '\061', '\012', '\156', '\112', '\144', '\040', '\141', '\156', + '\040', '\061', '\012', '\160', '\105', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\147', '\126', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\132', '\171', '\146', + '\040', '\156', '\171', '\040', '\061', '\012', '\156', '\155', + '\124', '\040', '\141', '\156', '\040', '\061', '\012', '\143', + '\163', '\121', '\040', '\143', '\150', '\040', '\061', '\012', + '\120', '\153', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\144', '\120', '\040', '\164', '\150', '\040', + '\061', '\012', '\146', '\153', '\172', '\040', '\163', '\172', + '\040', '\061', '\012', '\121', '\156', '\143', '\040', '\141', + '\156', '\040', '\061', '\012', '\160', '\102', '\152', '\040', + '\151', '\152', '\040', '\061', '\012', '\115', '\152', '\166', + '\040', '\151', '\152', '\040', '\061', '\012', '\171', '\155', + '\112', '\040', '\155', '\145', '\040', '\061', '\012', '\115', + '\170', '\163', '\040', '\163', '\164', '\040', '\061', '\012', + '\150', '\142', '\114', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\121', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\170', '\104', '\171', '\040', '\156', '\171', + '\040', '\061', '\012', '\144', '\152', '\103', '\040', '\144', + '\145', '\040', '\061', '\012', '\143', '\144', '\121', '\040', + '\143', '\150', '\040', '\061', '\012', '\142', '\156', '\114', + '\040', '\141', '\156', '\040', '\061', '\012', '\131', '\152', + '\154', '\040', '\154', '\145', '\040', '\061', '\012', '\161', + '\125', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\155', '\152', '\127', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\127', '\163', '\040', '\163', '\164', '\040', + '\061', '\012', '\170', '\166', '\106', '\040', '\166', '\141', + '\040', '\061', '\012', '\107', '\161', '\151', '\040', '\161', + '\165', '\040', '\061', '\012', '\146', '\107', '\155', '\040', + '\155', '\145', '\040', '\061', '\012', '\130', '\165', '\167', + '\040', '\161', '\165', '\040', '\061', '\012', '\161', '\103', + '\163', '\040', '\161', '\165', '\040', '\061', '\012', '\113', + '\170', '\155', '\040', '\155', '\145', '\040', '\061', '\012', + '\154', '\116', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\163', '\144', '\114', '\040', '\144', '\145', '\040', + '\061', '\012', '\126', '\164', '\156', '\040', '\164', '\150', + '\040', '\061', '\012', '\163', '\112', '\152', '\040', '\163', + '\164', '\040', '\061', '\012', '\153', '\121', '\152', '\040', + '\151', '\152', '\040', '\061', '\012', '\170', '\146', '\130', + '\040', '\146', '\157', '\040', '\061', '\012', '\116', '\161', + '\153', '\040', '\161', '\165', '\040', '\061', '\012', '\143', + '\102', '\163', '\040', '\143', '\150', '\040', '\061', '\012', + '\171', '\172', '\120', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\125', '\166', '\040', '\166', '\141', '\040', + '\061', '\012', '\154', '\142', '\124', '\040', '\154', '\145', + '\040', '\061', '\012', '\167', '\171', '\126', '\040', '\167', + '\141', '\040', '\061', '\012', '\130', '\153', '\155', '\040', + '\153', '\141', '\040', '\061', '\012', '\127', '\144', '\166', + '\040', '\144', '\145', '\040', '\061', '\012', '\161', '\121', + '\156', '\040', '\141', '\156', '\040', '\061', '\012', '\163', + '\161', '\132', '\040', '\161', '\165', '\040', '\061', '\012', + '\163', '\146', '\127', '\040', '\163', '\164', '\040', '\061', + '\012', '\147', '\146', '\115', '\040', '\156', '\147', '\040', + '\061', '\012', '\126', '\154', '\160', '\040', '\154', '\145', + '\040', '\061', '\012', '\130', '\152', '\170', '\040', '\151', + '\152', '\040', '\061', '\012', '\150', '\111', '\152', '\040', + '\164', '\150', '\040', '\061', '\012', '\112', '\167', '\163', + '\040', '\163', '\164', '\040', '\061', '\012', '\170', '\132', + '\162', '\040', '\145', '\162', '\040', '\061', '\012', '\151', + '\113', '\167', '\040', '\151', '\156', '\040', '\061', '\012', + '\124', '\142', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\172', '\121', '\166', '\040', '\163', '\172', '\040', + '\061', '\012', '\156', '\155', '\132', '\040', '\141', '\156', + '\040', '\061', '\012', '\142', '\160', '\105', '\040', '\160', + '\162', '\040', '\061', '\012', '\172', '\123', '\166', '\040', + '\163', '\172', '\040', '\061', '\012', '\106', '\147', '\151', + '\040', '\156', '\147', '\040', '\061', '\012', '\165', '\111', + '\167', '\040', '\161', '\165', '\040', '\061', '\012', '\132', + '\166', '\170', '\040', '\166', '\141', '\040', '\061', '\012', + '\162', '\161', '\122', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\152', '\132', '\040', '\151', '\152', '\040', + '\061', '\012', '\116', '\152', '\162', '\040', '\145', '\162', + '\040', '\061', '\012', '\153', '\167', '\106', '\040', '\153', + '\141', '\040', '\061', '\012', '\117', '\166', '\167', '\040', + '\166', '\141', '\040', '\061', '\012', '\150', '\167', '\132', + '\040', '\164', '\150', '\040', '\061', '\012', '\115', '\166', + '\153', '\040', '\153', '\141', '\040', '\061', '\012', '\104', + '\166', '\146', '\040', '\166', '\141', '\040', '\061', '\012', + '\170', '\163', '\120', '\040', '\163', '\164', '\040', '\061', + '\012', '\147', '\132', '\161', '\040', '\156', '\147', '\040', + '\061', '\012', '\166', '\130', '\166', '\040', '\166', '\141', + '\040', '\061', '\012', '\167', '\107', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\161', '\154', '\117', '\040', + '\161', '\165', '\040', '\061', '\012', '\146', '\116', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\116', '\166', + '\167', '\040', '\166', '\141', '\040', '\061', '\012', '\172', + '\144', '\132', '\040', '\144', '\145', '\040', '\061', '\012', + '\166', '\170', '\126', '\040', '\166', '\141', '\040', '\061', + '\012', '\116', '\150', '\172', '\040', '\164', '\150', '\040', + '\061', '\012', '\164', '\132', '\155', '\040', '\164', '\150', + '\040', '\061', '\012', '\151', '\171', '\123', '\040', '\151', + '\156', '\040', '\061', '\012', '\161', '\132', '\141', '\040', + '\141', '\156', '\040', '\061', '\012', '\170', '\162', '\132', + '\040', '\145', '\162', '\040', '\061', '\012', '\161', '\154', + '\171', '\040', '\161', '\165', '\040', '\061', '\012', '\143', + '\152', '\115', '\040', '\143', '\150', '\040', '\061', '\012', + '\153', '\131', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\151', '\171', '\106', '\040', '\151', '\156', '\040', + '\061', '\012', '\103', '\144', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\170', '\167', '\105', '\040', '\167', + '\141', '\040', '\061', '\012', '\170', '\146', '\126', '\040', + '\146', '\157', '\040', '\061', '\012', '\167', '\142', '\106', + '\040', '\167', '\141', '\040', '\061', '\012', '\167', '\165', + '\117', '\040', '\161', '\165', '\040', '\061', '\012', '\122', + '\154', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\146', '\103', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\143', '\132', '\040', '\143', '\150', '\040', + '\061', '\012', '\107', '\152', '\166', '\040', '\151', '\152', + '\040', '\061', '\012', '\147', '\114', '\154', '\040', '\156', + '\147', '\040', '\061', '\012', '\167', '\114', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\172', '\155', '\120', + '\040', '\163', '\172', '\040', '\061', '\012', '\143', '\131', + '\157', '\040', '\143', '\150', '\040', '\061', '\012', '\122', + '\150', '\153', '\040', '\164', '\150', '\040', '\061', '\012', + '\147', '\162', '\115', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\104', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\131', '\171', '\142', '\040', '\142', '\145', + '\040', '\061', '\012', '\165', '\171', '\127', '\040', '\165', + '\156', '\040', '\061', '\012', '\153', '\107', '\142', '\040', + '\153', '\141', '\040', '\061', '\012', '\151', '\167', '\113', + '\040', '\151', '\156', '\040', '\061', '\012', '\161', '\153', + '\116', '\040', '\161', '\165', '\040', '\061', '\012', '\161', + '\130', '\144', '\040', '\161', '\165', '\040', '\061', '\012', + '\172', '\103', '\142', '\040', '\163', '\172', '\040', '\061', + '\012', '\162', '\121', '\146', '\040', '\145', '\162', '\040', + '\061', '\012', '\170', '\162', '\117', '\040', '\145', '\162', + '\040', '\061', '\012', '\106', '\172', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\167', '\123', '\152', '\040', + '\151', '\152', '\040', '\061', '\012', '\171', '\120', '\167', + '\040', '\167', '\141', '\040', '\061', '\012', '\102', '\161', + '\167', '\040', '\161', '\165', '\040', '\061', '\012', '\153', + '\127', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\161', '\150', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\102', '\167', '\040', '\153', '\141', '\040', + '\061', '\012', '\171', '\166', '\114', '\040', '\166', '\141', + '\040', '\061', '\012', '\170', '\143', '\124', '\040', '\143', + '\150', '\040', '\061', '\012', '\106', '\142', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\143', '\105', '\142', + '\040', '\143', '\150', '\040', '\061', '\012', '\166', '\105', + '\153', '\040', '\153', '\141', '\040', '\061', '\012', '\165', + '\121', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\163', '\110', '\167', '\040', '\165', '\163', '\040', '\061', + '\012', '\106', '\166', '\146', '\040', '\166', '\141', '\040', + '\061', '\012', '\167', '\153', '\117', '\040', '\153', '\141', + '\040', '\061', '\012', '\167', '\151', '\131', '\040', '\151', + '\156', '\040', '\061', '\012', '\163', '\120', '\155', '\040', + '\163', '\164', '\040', '\061', '\012', '\144', '\106', '\156', + '\040', '\141', '\156', '\040', '\061', '\012', '\161', '\121', + '\170', '\040', '\161', '\165', '\040', '\061', '\012', '\122', + '\163', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\146', '\125', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\164', '\114', '\167', '\040', '\164', '\150', '\040', + '\061', '\012', '\163', '\122', '\153', '\040', '\163', '\164', + '\040', '\061', '\012', '\172', '\153', '\120', '\040', '\163', + '\172', '\040', '\061', '\012', '\155', '\166', '\106', '\040', + '\166', '\141', '\040', '\061', '\012', '\152', '\131', '\142', + '\040', '\151', '\152', '\040', '\061', '\012', '\163', '\167', + '\131', '\040', '\151', '\163', '\040', '\061', '\012', '\162', + '\122', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\162', '\110', '\144', '\040', '\145', '\162', '\040', '\061', + '\012', '\142', '\104', '\153', '\040', '\153', '\141', '\040', + '\061', '\012', '\154', '\127', '\166', '\040', '\154', '\145', + '\040', '\061', '\012', '\166', '\161', '\166', '\040', '\161', + '\165', '\040', '\061', '\012', '\161', '\157', '\116', '\040', + '\161', '\165', '\040', '\061', '\012', '\172', '\115', '\154', + '\040', '\154', '\145', '\040', '\061', '\012', '\160', '\146', + '\112', '\040', '\160', '\162', '\040', '\061', '\012', '\104', + '\155', '\172', '\040', '\163', '\172', '\040', '\061', '\012', + '\157', '\142', '\121', '\040', '\157', '\156', '\040', '\061', + '\012', '\126', '\146', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\142', '\126', '\144', '\040', '\144', '\145', + '\040', '\061', '\012', '\103', '\152', '\166', '\040', '\151', + '\152', '\040', '\061', '\012', '\155', '\113', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\152', '\152', '\105', + '\040', '\151', '\152', '\040', '\061', '\012', '\101', '\161', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\103', + '\170', '\156', '\040', '\141', '\156', '\040', '\061', '\012', + '\166', '\160', '\110', '\040', '\166', '\141', '\040', '\061', + '\012', '\114', '\170', '\141', '\040', '\141', '\156', '\040', + '\061', '\012', '\172', '\160', '\110', '\040', '\163', '\172', + '\040', '\061', '\012', '\161', '\157', '\106', '\040', '\161', + '\165', '\040', '\061', '\012', '\150', '\122', '\172', '\040', + '\164', '\150', '\040', '\061', '\012', '\171', '\131', '\167', + '\040', '\167', '\141', '\040', '\061', '\012', '\144', '\125', + '\170', '\040', '\144', '\145', '\040', '\061', '\012', '\113', + '\170', '\154', '\040', '\154', '\145', '\040', '\061', '\012', + '\170', '\125', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\150', '\104', '\160', '\040', '\164', '\150', '\040', + '\061', '\012', '\172', '\104', '\146', '\040', '\163', '\172', + '\040', '\061', '\012', '\127', '\163', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\152', '\172', '\132', '\040', + '\163', '\172', '\040', '\061', '\012', '\155', '\107', '\146', + '\040', '\155', '\145', '\040', '\061', '\012', '\152', '\152', + '\126', '\040', '\151', '\152', '\040', '\061', '\012', '\160', + '\146', '\122', '\040', '\160', '\162', '\040', '\061', '\012', + '\142', '\120', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\152', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\122', '\152', '\170', '\040', '\151', '\152', + '\040', '\061', '\012', '\114', '\167', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\146', '\161', '\110', '\040', + '\161', '\165', '\040', '\061', '\012', '\152', '\122', '\163', + '\040', '\163', '\172', '\040', '\061', '\012', '\163', '\146', + '\124', '\040', '\163', '\172', '\040', '\061', '\012', '\107', + '\162', '\167', '\040', '\145', '\162', '\040', '\061', '\012', + '\172', '\107', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\143', '\127', '\040', '\143', '\150', '\040', + '\061', '\012', '\154', '\125', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\160', '\122', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\156', '\132', '\161', '\040', + '\141', '\156', '\040', '\061', '\012', '\123', '\166', '\170', + '\040', '\166', '\141', '\040', '\061', '\012', '\120', '\150', + '\146', '\040', '\164', '\150', '\040', '\061', '\012', '\106', + '\166', '\152', '\040', '\151', '\152', '\040', '\061', '\012', + '\121', '\154', '\155', '\040', '\154', '\145', '\040', '\061', + '\012', '\152', '\147', '\123', '\040', '\156', '\147', '\040', + '\061', '\012', '\115', '\155', '\166', '\040', '\166', '\141', + '\040', '\061', '\012', '\170', '\120', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\161', '\161', '\167', '\040', + '\161', '\165', '\040', '\061', '\012', '\162', '\127', '\160', + '\040', '\145', '\162', '\040', '\061', '\012', '\161', '\111', + '\162', '\040', '\161', '\165', '\040', '\061', '\012', '\103', + '\170', '\146', '\040', '\146', '\157', '\040', '\061', '\012', + '\167', '\164', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\113', '\142', '\040', '\143', '\150', '\040', + '\061', '\012', '\142', '\164', '\114', '\040', '\164', '\150', + '\040', '\061', '\012', '\160', '\122', '\170', '\040', '\160', + '\162', '\040', '\061', '\012', '\172', '\163', '\102', '\040', + '\163', '\172', '\040', '\061', '\012', '\156', '\142', '\104', + '\040', '\141', '\156', '\040', '\061', '\012', '\152', '\113', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\142', + '\150', '\114', '\040', '\164', '\150', '\040', '\061', '\012', + '\131', '\150', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\131', '\162', '\040', '\145', '\162', '\040', + '\061', '\012', '\152', '\103', '\155', '\040', '\151', '\152', + '\040', '\061', '\012', '\170', '\172', '\113', '\040', '\163', + '\172', '\040', '\061', '\012', '\160', '\112', '\154', '\040', + '\154', '\145', '\040', '\061', '\012', '\121', '\162', '\162', + '\040', '\145', '\162', '\040', '\061', '\012', '\165', '\166', + '\107', '\040', '\161', '\165', '\040', '\061', '\012', '\143', + '\146', '\112', '\040', '\143', '\150', '\040', '\061', '\012', + '\151', '\161', '\130', '\040', '\151', '\156', '\040', '\061', + '\012', '\166', '\116', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\161', '\143', '\115', '\040', '\143', '\150', + '\040', '\061', '\012', '\127', '\166', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\166', '\155', '\123', '\040', + '\166', '\141', '\040', '\061', '\012', '\166', '\127', '\160', + '\040', '\166', '\141', '\040', '\061', '\012', '\141', '\111', + '\152', '\040', '\141', '\156', '\040', '\061', '\012', '\152', + '\155', '\123', '\040', '\151', '\152', '\040', '\061', '\012', + '\106', '\155', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\151', '\171', '\116', '\040', '\151', '\156', '\040', + '\061', '\012', '\142', '\132', '\165', '\040', '\161', '\165', + '\040', '\061', '\012', '\113', '\172', '\152', '\040', '\163', + '\172', '\040', '\061', '\012', '\126', '\167', '\144', '\040', + '\144', '\145', '\040', '\061', '\012', '\125', '\154', '\170', + '\040', '\154', '\145', '\040', '\061', '\012', '\162', '\103', + '\166', '\040', '\145', '\162', '\040', '\061', '\012', '\167', + '\166', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\121', '\153', '\162', '\040', '\162', '\151', '\040', '\061', + '\012', '\146', '\152', '\103', '\040', '\151', '\152', '\040', + '\061', '\012', '\164', '\122', '\162', '\040', '\164', '\150', + '\040', '\061', '\012', '\160', '\103', '\171', '\040', '\160', + '\162', '\040', '\061', '\012', '\146', '\142', '\103', '\040', + '\142', '\145', '\040', '\061', '\012', '\146', '\121', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\130', '\153', + '\146', '\040', '\153', '\141', '\040', '\061', '\012', '\104', + '\161', '\162', '\040', '\161', '\165', '\040', '\061', '\012', + '\146', '\147', '\105', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\115', '\155', '\040', '\166', '\141', '\040', + '\061', '\012', '\144', '\120', '\142', '\040', '\144', '\145', + '\040', '\061', '\012', '\166', '\152', '\114', '\040', '\151', + '\152', '\040', '\061', '\012', '\167', '\113', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\120', '\171', '\167', + '\040', '\167', '\141', '\040', '\061', '\012', '\145', '\130', + '\166', '\040', '\145', '\162', '\040', '\061', '\012', '\156', + '\126', '\167', '\040', '\141', '\156', '\040', '\061', '\012', + '\112', '\167', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\104', '\146', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\164', '\103', '\143', '\040', '\164', '\150', + '\040', '\061', '\012', '\161', '\164', '\110', '\040', '\164', + '\150', '\040', '\061', '\012', '\130', '\161', '\155', '\040', + '\161', '\165', '\040', '\061', '\012', '\102', '\150', '\143', + '\040', '\164', '\150', '\040', '\061', '\012', '\164', '\143', + '\130', '\040', '\164', '\150', '\040', '\061', '\012', '\170', + '\113', '\160', '\040', '\160', '\162', '\040', '\061', '\012', + '\164', '\146', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\142', '\132', '\040', '\151', '\156', '\040', + '\061', '\012', '\116', '\172', '\142', '\040', '\163', '\172', + '\040', '\061', '\012', '\127', '\156', '\152', '\040', '\141', + '\156', '\040', '\061', '\012', '\166', '\130', '\171', '\040', + '\166', '\141', '\040', '\061', '\012', '\151', '\126', '\146', + '\040', '\151', '\156', '\040', '\061', '\012', '\144', '\170', + '\124', '\040', '\144', '\145', '\040', '\061', '\012', '\152', + '\170', '\121', '\040', '\151', '\152', '\040', '\061', '\012', + '\104', '\144', '\166', '\040', '\144', '\145', '\040', '\061', + '\012', '\155', '\130', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\146', '\125', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\167', '\147', '\121', '\040', '\156', + '\147', '\040', '\061', '\012', '\114', '\147', '\152', '\040', + '\156', '\147', '\040', '\061', '\012', '\155', '\147', '\131', + '\040', '\156', '\147', '\040', '\061', '\012', '\161', '\115', + '\167', '\040', '\161', '\165', '\040', '\061', '\012', '\147', + '\160', '\112', '\040', '\156', '\147', '\040', '\061', '\012', + '\163', '\132', '\170', '\040', '\163', '\164', '\040', '\061', + '\012', '\156', '\130', '\172', '\040', '\141', '\156', '\040', + '\061', '\012', '\127', '\166', '\145', '\040', '\145', '\162', + '\040', '\061', '\012', '\154', '\126', '\153', '\040', '\154', + '\145', '\040', '\061', '\012', '\167', '\103', '\142', '\040', + '\167', '\141', '\040', '\061', '\012', '\170', '\166', '\111', + '\040', '\166', '\141', '\040', '\061', '\012', '\155', '\146', + '\112', '\040', '\155', '\145', '\040', '\061', '\012', '\164', + '\121', '\161', '\040', '\164', '\150', '\040', '\061', '\012', + '\144', '\124', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\161', '\153', '\040', '\161', '\165', '\040', + '\061', '\012', '\156', '\126', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\167', '\111', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\121', '\166', '\160', '\040', + '\166', '\141', '\040', '\061', '\012', '\166', '\146', '\116', + '\040', '\166', '\141', '\040', '\061', '\012', '\147', '\121', + '\163', '\040', '\156', '\147', '\040', '\061', '\012', '\151', + '\126', '\160', '\040', '\151', '\156', '\040', '\061', '\012', + '\152', '\107', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\115', '\146', '\040', '\146', '\157', '\040', + '\061', '\012', '\170', '\166', '\167', '\040', '\167', '\151', + '\040', '\061', '\012', '\172', '\111', '\154', '\040', '\154', + '\145', '\040', '\061', '\012', '\172', '\146', '\122', '\040', + '\163', '\172', '\040', '\061', '\012', '\172', '\127', '\166', + '\040', '\163', '\172', '\040', '\061', '\012', '\145', '\150', + '\126', '\040', '\164', '\150', '\040', '\061', '\012', '\144', + '\132', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\164', '\155', '\113', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\114', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\160', '\132', '\142', '\040', '\160', '\162', + '\040', '\061', '\012', '\166', '\156', '\112', '\040', '\141', + '\156', '\040', '\061', '\012', '\146', '\166', '\153', '\040', + '\153', '\141', '\040', '\061', '\012', '\130', '\150', '\166', + '\040', '\164', '\150', '\040', '\061', '\012', '\126', '\152', + '\156', '\040', '\141', '\156', '\040', '\061', '\012', '\164', + '\147', '\111', '\040', '\164', '\150', '\040', '\061', '\012', + '\170', '\141', '\112', '\040', '\141', '\156', '\040', '\061', + '\012', '\155', '\123', '\146', '\040', '\155', '\145', '\040', + '\061', '\012', '\130', '\172', '\155', '\040', '\163', '\172', + '\040', '\061', '\012', '\144', '\124', '\172', '\040', '\144', + '\145', '\040', '\061', '\012', '\170', '\130', '\155', '\040', + '\155', '\145', '\040', '\061', '\012', '\160', '\121', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\103', '\161', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\142', + '\123', '\163', '\040', '\163', '\164', '\040', '\061', '\012', + '\160', '\162', '\127', '\040', '\145', '\162', '\040', '\061', + '\012', '\150', '\104', '\142', '\040', '\164', '\150', '\040', + '\061', '\012', '\163', '\130', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\153', '\143', '\104', '\040', '\143', + '\150', '\040', '\061', '\012', '\153', '\147', '\132', '\040', + '\156', '\147', '\040', '\061', '\012', '\124', '\172', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\172', '\143', + '\122', '\040', '\143', '\150', '\040', '\061', '\012', '\130', + '\167', '\165', '\040', '\161', '\165', '\040', '\061', '\012', + '\153', '\130', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\131', '\167', '\166', '\040', '\167', '\151', '\040', + '\061', '\012', '\162', '\160', '\113', '\040', '\145', '\162', + '\040', '\061', '\012', '\167', '\120', '\163', '\040', '\151', + '\163', '\040', '\061', '\012', '\113', '\152', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\146', '\104', '\142', + '\040', '\142', '\145', '\040', '\061', '\012', '\152', '\162', + '\106', '\040', '\145', '\162', '\040', '\061', '\012', '\142', + '\142', '\121', '\040', '\142', '\145', '\040', '\061', '\012', + '\121', '\144', '\142', '\040', '\144', '\145', '\040', '\061', + '\012', '\162', '\113', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\166', '\131', '\146', '\040', '\166', '\141', + '\040', '\061', '\012', '\166', '\170', '\101', '\040', '\166', + '\141', '\040', '\061', '\012', '\146', '\150', '\115', '\040', + '\164', '\150', '\040', '\061', '\012', '\152', '\163', '\125', + '\040', '\163', '\164', '\040', '\061', '\012', '\172', '\130', + '\153', '\040', '\163', '\172', '\040', '\061', '\012', '\165', + '\167', '\117', '\040', '\161', '\165', '\040', '\061', '\012', + '\152', '\163', '\122', '\040', '\163', '\164', '\040', '\061', + '\012', '\153', '\110', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\170', '\127', '\166', '\040', '\166', '\141', + '\040', '\061', '\012', '\166', '\146', '\123', '\040', '\166', + '\141', '\040', '\061', '\012', '\160', '\111', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\142', '\143', '\127', + '\040', '\143', '\150', '\040', '\061', '\012', '\172', '\144', + '\115', '\040', '\163', '\172', '\040', '\061', '\012', '\147', + '\103', '\172', '\040', '\156', '\147', '\040', '\061', '\012', + '\150', '\172', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\121', '\167', '\040', '\167', '\141', '\040', + '\061', '\012', '\157', '\152', '\130', '\040', '\157', '\156', + '\040', '\061', '\012', '\126', '\161', '\166', '\040', '\161', + '\165', '\040', '\061', '\012', '\161', '\127', '\142', '\040', + '\161', '\165', '\040', '\061', '\012', '\131', '\153', '\142', + '\040', '\153', '\141', '\040', '\061', '\012', '\170', '\156', + '\112', '\040', '\141', '\156', '\040', '\061', '\012', '\163', + '\112', '\172', '\040', '\163', '\164', '\040', '\061', '\012', + '\150', '\122', '\162', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\130', '\163', '\040', '\164', '\150', '\040', + '\061', '\012', '\121', '\145', '\142', '\040', '\145', '\162', + '\040', '\061', '\012', '\125', '\167', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\156', '\131', '\147', '\040', + '\141', '\156', '\040', '\061', '\012', '\131', '\146', '\170', + '\040', '\146', '\157', '\040', '\061', '\012', '\170', '\162', + '\107', '\040', '\145', '\162', '\040', '\061', '\012', '\145', + '\132', '\162', '\040', '\154', '\145', '\040', '\061', '\012', + '\165', '\146', '\126', '\040', '\165', '\163', '\040', '\061', + '\012', '\162', '\130', '\155', '\040', '\145', '\162', '\040', + '\061', '\012', '\161', '\132', '\166', '\040', '\161', '\165', + '\040', '\061', '\012', '\166', '\121', '\172', '\040', '\163', + '\172', '\040', '\061', '\012', '\124', '\156', '\161', '\040', + '\141', '\156', '\040', '\061', '\012', '\122', '\155', '\152', + '\040', '\151', '\152', '\040', '\061', '\012', '\152', '\154', + '\115', '\040', '\154', '\145', '\040', '\061', '\012', '\143', + '\161', '\117', '\040', '\143', '\150', '\040', '\061', '\012', + '\170', '\127', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\152', '\143', '\132', '\040', '\143', '\150', '\040', + '\061', '\012', '\152', '\146', '\126', '\040', '\151', '\152', + '\040', '\061', '\012', '\132', '\155', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\142', '\170', '\115', '\040', + '\142', '\145', '\040', '\061', '\012', '\146', '\106', '\144', + '\040', '\144', '\145', '\040', '\061', '\012', '\147', '\152', + '\120', '\040', '\156', '\147', '\040', '\061', '\012', '\150', + '\115', '\163', '\040', '\164', '\150', '\040', '\061', '\012', + '\131', '\163', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\153', '\126', '\040', '\161', '\165', '\040', + '\061', '\012', '\113', '\155', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\170', '\131', '\171', '\040', '\156', + '\171', '\040', '\061', '\012', '\144', '\166', '\130', '\040', + '\144', '\145', '\040', '\061', '\012', '\162', '\167', '\103', + '\040', '\145', '\162', '\040', '\061', '\012', '\147', '\167', + '\127', '\040', '\167', '\141', '\040', '\061', '\012', '\121', + '\160', '\171', '\040', '\160', '\162', '\040', '\061', '\012', + '\152', '\130', '\171', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\117', '\152', '\040', '\161', '\165', '\040', + '\061', '\012', '\121', '\155', '\172', '\040', '\163', '\172', + '\040', '\061', '\012', '\105', '\161', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\172', '\112', '\163', '\040', + '\163', '\164', '\040', '\061', '\012', '\146', '\110', '\171', + '\040', '\156', '\171', '\040', '\061', '\012', '\150', '\104', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\163', + '\104', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\126', '\153', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\114', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\166', '\110', '\155', '\040', '\166', '\141', + '\040', '\061', '\012', '\166', '\156', '\130', '\040', '\141', + '\156', '\040', '\061', '\012', '\152', '\170', '\123', '\040', + '\151', '\152', '\040', '\061', '\012', '\112', '\164', '\152', + '\040', '\164', '\150', '\040', '\061', '\012', '\161', '\147', + '\105', '\040', '\156', '\147', '\040', '\061', '\012', '\142', + '\160', '\110', '\040', '\160', '\162', '\040', '\061', '\012', + '\111', '\161', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\115', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\144', '\155', '\105', '\040', '\144', '\145', + '\040', '\061', '\012', '\110', '\146', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\160', '\123', '\142', '\040', + '\160', '\162', '\040', '\061', '\012', '\170', '\150', '\111', + '\040', '\164', '\150', '\040', '\061', '\012', '\121', '\152', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\171', + '\146', '\130', '\040', '\156', '\171', '\040', '\061', '\012', + '\166', '\165', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\106', '\167', '\040', '\167', '\141', '\040', + '\061', '\012', '\172', '\156', '\123', '\040', '\141', '\156', + '\040', '\061', '\012', '\172', '\154', '\126', '\040', '\154', + '\145', '\040', '\061', '\012', '\154', '\153', '\113', '\040', + '\154', '\145', '\040', '\061', '\012', '\106', '\166', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\161', '\152', + '\124', '\040', '\161', '\165', '\040', '\061', '\012', '\172', + '\157', '\121', '\040', '\157', '\156', '\040', '\061', '\012', + '\127', '\166', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\115', '\156', '\040', '\164', '\150', '\040', + '\061', '\012', '\144', '\115', '\167', '\040', '\144', '\145', + '\040', '\061', '\012', '\147', '\143', '\106', '\040', '\143', + '\150', '\040', '\061', '\012', '\144', '\142', '\102', '\040', + '\144', '\145', '\040', '\061', '\012', '\103', '\161', '\152', + '\040', '\161', '\165', '\040', '\061', '\012', '\155', '\103', + '\166', '\040', '\166', '\141', '\040', '\061', '\012', '\160', + '\112', '\170', '\040', '\160', '\162', '\040', '\061', '\012', + '\104', '\146', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\163', '\152', '\114', '\040', '\163', '\164', '\040', + '\061', '\012', '\161', '\151', '\107', '\040', '\151', '\156', + '\040', '\061', '\012', '\132', '\154', '\163', '\040', '\154', + '\145', '\040', '\061', '\012', '\126', '\163', '\146', '\040', + '\163', '\164', '\040', '\061', '\012', '\106', '\147', '\144', + '\040', '\156', '\147', '\040', '\061', '\012', '\167', '\155', + '\104', '\040', '\155', '\145', '\040', '\061', '\012', '\104', + '\170', '\157', '\040', '\157', '\156', '\040', '\061', '\012', + '\161', '\162', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\112', '\162', '\040', '\145', '\162', '\040', + '\061', '\012', '\143', '\114', '\170', '\040', '\143', '\150', + '\040', '\061', '\012', '\152', '\144', '\102', '\040', '\144', + '\145', '\040', '\061', '\012', '\171', '\142', '\115', '\040', + '\142', '\145', '\040', '\061', '\012', '\155', '\166', '\115', + '\040', '\166', '\141', '\040', '\061', '\012', '\152', '\164', + '\130', '\040', '\164', '\150', '\040', '\061', '\012', '\143', + '\156', '\102', '\040', '\141', '\156', '\040', '\061', '\012', + '\167', '\164', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\113', '\163', '\144', '\040', '\163', '\164', '\040', + '\061', '\012', '\167', '\161', '\154', '\040', '\167', '\141', + '\040', '\061', '\012', '\155', '\150', '\125', '\040', '\164', + '\150', '\040', '\061', '\012', '\157', '\112', '\171', '\040', + '\157', '\156', '\040', '\061', '\012', '\107', '\150', '\160', + '\040', '\164', '\150', '\040', '\061', '\012', '\161', '\157', + '\130', '\040', '\161', '\165', '\040', '\061', '\012', '\170', + '\163', '\111', '\040', '\163', '\164', '\040', '\061', '\012', + '\166', '\106', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\146', '\131', '\145', '\040', '\145', '\162', '\040', + '\061', '\012', '\154', '\156', '\126', '\040', '\141', '\156', + '\040', '\061', '\012', '\165', '\130', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\105', '\157', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\167', '\143', '\115', + '\040', '\167', '\141', '\040', '\061', '\012', '\152', '\167', + '\113', '\040', '\151', '\152', '\040', '\061', '\012', '\107', + '\153', '\145', '\040', '\145', '\162', '\040', '\061', '\012', + '\165', '\106', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\143', '\147', '\040', '\143', '\150', '\040', + '\061', '\012', '\170', '\161', '\171', '\040', '\161', '\165', + '\040', '\061', '\012', '\142', '\164', '\115', '\040', '\164', + '\150', '\040', '\061', '\012', '\152', '\110', '\167', '\040', + '\151', '\152', '\040', '\061', '\012', '\161', '\145', '\125', + '\040', '\161', '\165', '\040', '\061', '\012', '\121', '\152', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\156', + '\165', '\121', '\040', '\141', '\156', '\040', '\061', '\012', + '\106', '\143', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\113', '\161', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\114', '\161', '\166', '\040', '\161', '\165', + '\040', '\061', '\012', '\155', '\167', '\125', '\040', '\155', + '\145', '\040', '\061', '\012', '\146', '\121', '\163', '\040', + '\163', '\164', '\040', '\061', '\012', '\153', '\123', '\144', + '\040', '\144', '\145', '\040', '\061', '\012', '\156', '\131', + '\166', '\040', '\141', '\156', '\040', '\061', '\012', '\167', + '\107', '\152', '\040', '\151', '\152', '\040', '\061', '\012', + '\147', '\166', '\132', '\040', '\156', '\147', '\040', '\061', + '\012', '\155', '\161', '\116', '\040', '\161', '\165', '\040', + '\061', '\012', '\106', '\150', '\160', '\040', '\164', '\150', + '\040', '\061', '\012', '\160', '\115', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\144', '\102', '\150', '\040', + '\143', '\150', '\040', '\061', '\012', '\142', '\130', '\153', + '\040', '\153', '\141', '\040', '\061', '\012', '\146', '\161', + '\113', '\040', '\161', '\165', '\040', '\061', '\012', '\131', + '\171', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\113', '\162', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\122', '\156', '\166', '\040', '\141', '\156', '\040', + '\061', '\012', '\165', '\165', '\105', '\040', '\161', '\165', + '\040', '\061', '\012', '\130', '\163', '\172', '\040', '\163', + '\164', '\040', '\061', '\012', '\146', '\113', '\142', '\040', + '\142', '\145', '\040', '\061', '\012', '\171', '\111', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\116', '\143', + '\144', '\040', '\143', '\150', '\040', '\061', '\012', '\155', + '\114', '\162', '\040', '\145', '\162', '\040', '\061', '\012', + '\143', '\123', '\163', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\142', '\105', '\040', '\154', '\145', '\040', + '\061', '\012', '\170', '\141', '\127', '\040', '\141', '\156', + '\040', '\061', '\012', '\122', '\164', '\144', '\040', '\164', + '\150', '\040', '\061', '\012', '\162', '\142', '\106', '\040', + '\145', '\162', '\040', '\061', '\012', '\166', '\147', '\122', + '\040', '\156', '\147', '\040', '\061', '\012', '\163', '\143', + '\132', '\040', '\143', '\150', '\040', '\061', '\012', '\162', + '\110', '\160', '\040', '\145', '\162', '\040', '\061', '\012', + '\145', '\131', '\167', '\040', '\145', '\162', '\040', '\061', + '\012', '\114', '\170', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\161', '\122', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\152', '\160', '\116', '\040', '\151', + '\152', '\040', '\061', '\012', '\162', '\152', '\127', '\040', + '\145', '\162', '\040', '\061', '\012', '\154', '\147', '\113', + '\040', '\156', '\147', '\040', '\061', '\012', '\155', '\103', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\146', + '\107', '\165', '\040', '\161', '\165', '\040', '\061', '\012', + '\170', '\172', '\124', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\121', '\167', '\040', '\167', '\141', '\040', + '\061', '\012', '\153', '\154', '\112', '\040', '\154', '\151', + '\040', '\061', '\012', '\143', '\161', '\153', '\040', '\143', + '\150', '\040', '\061', '\012', '\154', '\115', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\160', '\131', '\163', + '\040', '\163', '\164', '\040', '\061', '\012', '\150', '\121', + '\153', '\040', '\164', '\150', '\040', '\061', '\012', '\110', + '\170', '\172', '\040', '\163', '\172', '\040', '\061', '\012', + '\146', '\145', '\131', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\150', '\106', '\040', '\164', '\150', '\040', + '\061', '\012', '\146', '\102', '\155', '\040', '\155', '\145', + '\040', '\061', '\012', '\146', '\126', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\172', '\146', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\163', '\142', '\124', + '\040', '\163', '\164', '\040', '\061', '\012', '\144', '\121', + '\171', '\040', '\144', '\145', '\040', '\061', '\012', '\106', + '\155', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\166', '\150', '\114', '\040', '\164', '\150', '\040', '\061', + '\012', '\112', '\164', '\142', '\040', '\164', '\150', '\040', + '\061', '\012', '\126', '\162', '\170', '\040', '\145', '\162', + '\040', '\061', '\012', '\171', '\161', '\132', '\040', '\161', + '\165', '\040', '\061', '\012', '\152', '\104', '\155', '\040', + '\151', '\152', '\040', '\061', '\012', '\155', '\146', '\126', + '\040', '\155', '\145', '\040', '\061', '\012', '\157', '\123', + '\170', '\040', '\157', '\156', '\040', '\061', '\012', '\112', + '\170', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\167', '\117', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\112', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\126', '\166', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\105', '\161', '\145', '\040', '\161', + '\165', '\040', '\061', '\012', '\152', '\161', '\117', '\040', + '\161', '\165', '\040', '\061', '\012', '\172', '\170', '\111', + '\040', '\163', '\172', '\040', '\061', '\012', '\161', '\113', + '\146', '\040', '\161', '\165', '\040', '\061', '\012', '\146', + '\144', '\127', '\040', '\144', '\145', '\040', '\061', '\012', + '\143', '\143', '\115', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\143', '\127', '\040', '\143', '\150', '\040', + '\061', '\012', '\154', '\106', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\122', '\166', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\172', '\156', '\116', '\040', + '\141', '\156', '\040', '\061', '\012', '\172', '\142', '\125', + '\040', '\163', '\172', '\040', '\061', '\012', '\164', '\116', + '\167', '\040', '\164', '\150', '\040', '\061', '\012', '\167', + '\152', '\113', '\040', '\151', '\152', '\040', '\061', '\012', + '\112', '\142', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\102', '\146', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\161', '\145', '\130', '\040', '\154', '\145', + '\040', '\061', '\012', '\164', '\130', '\153', '\040', '\164', + '\150', '\040', '\061', '\012', '\163', '\154', '\112', '\040', + '\154', '\145', '\040', '\061', '\012', '\143', '\113', '\144', + '\040', '\143', '\150', '\040', '\061', '\012', '\156', '\103', + '\146', '\040', '\141', '\156', '\040', '\061', '\012', '\161', + '\147', '\126', '\040', '\156', '\147', '\040', '\061', '\012', + '\115', '\150', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\113', '\146', '\040', '\163', '\164', '\040', + '\061', '\012', '\150', '\161', '\132', '\040', '\164', '\150', + '\040', '\061', '\012', '\106', '\144', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\161', '\172', '\112', '\040', + '\161', '\165', '\040', '\061', '\012', '\163', '\116', '\156', + '\040', '\141', '\156', '\040', '\061', '\012', '\164', '\152', + '\127', '\040', '\164', '\150', '\040', '\061', '\012', '\170', + '\143', '\116', '\040', '\143', '\150', '\040', '\061', '\012', + '\146', '\143', '\112', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\152', '\125', '\040', '\144', '\145', '\040', + '\061', '\012', '\131', '\147', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\167', '\157', '\111', '\040', '\157', + '\156', '\040', '\061', '\012', '\131', '\171', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\153', '\121', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\150', '\146', + '\121', '\040', '\164', '\150', '\040', '\061', '\012', '\156', + '\162', '\114', '\040', '\141', '\156', '\040', '\061', '\012', + '\154', '\121', '\163', '\040', '\154', '\145', '\040', '\061', + '\012', '\155', '\164', '\106', '\040', '\164', '\150', '\040', + '\061', '\012', '\167', '\142', '\130', '\040', '\167', '\141', + '\040', '\061', '\012', '\147', '\155', '\122', '\040', '\156', + '\147', '\040', '\061', '\012', '\132', '\163', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\171', '\164', '\121', + '\040', '\164', '\150', '\040', '\061', '\012', '\155', '\142', + '\106', '\040', '\155', '\145', '\040', '\061', '\012', '\146', + '\147', '\124', '\040', '\156', '\147', '\040', '\061', '\012', + '\143', '\127', '\165', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\170', '\107', '\040', '\156', '\147', '\040', + '\061', '\012', '\150', '\116', '\166', '\040', '\164', '\150', + '\040', '\061', '\012', '\144', '\146', '\127', '\040', '\144', + '\145', '\040', '\061', '\012', '\172', '\162', '\103', '\040', + '\145', '\162', '\040', '\061', '\012', '\167', '\157', '\130', + '\040', '\157', '\156', '\040', '\061', '\012', '\167', '\152', + '\124', '\040', '\151', '\152', '\040', '\061', '\012', '\120', + '\161', '\167', '\040', '\161', '\165', '\040', '\061', '\012', + '\166', '\153', '\146', '\040', '\153', '\141', '\040', '\061', + '\012', '\156', '\114', '\172', '\040', '\141', '\156', '\040', + '\061', '\012', '\143', '\152', '\126', '\040', '\143', '\150', + '\040', '\061', '\012', '\146', '\143', '\120', '\040', '\143', + '\150', '\040', '\061', '\012', '\166', '\154', '\121', '\040', + '\154', '\145', '\040', '\061', '\012', '\106', '\147', '\161', + '\040', '\156', '\147', '\040', '\061', '\012', '\150', '\147', + '\120', '\040', '\164', '\150', '\040', '\061', '\012', '\107', + '\161', '\171', '\040', '\161', '\165', '\040', '\061', '\012', + '\164', '\113', '\163', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\146', '\166', '\040', '\166', '\141', '\040', + '\061', '\012', '\171', '\132', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\171', '\151', '\132', '\040', '\151', + '\156', '\040', '\061', '\012', '\162', '\130', '\166', '\040', + '\145', '\162', '\040', '\061', '\012', '\131', '\143', '\171', + '\040', '\143', '\150', '\040', '\061', '\012', '\146', '\166', + '\101', '\040', '\166', '\141', '\040', '\061', '\012', '\124', + '\161', '\163', '\040', '\161', '\165', '\040', '\061', '\012', + '\150', '\132', '\171', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\167', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\161', '\126', '\146', '\040', '\161', '\165', + '\040', '\061', '\012', '\115', '\150', '\161', '\040', '\164', + '\150', '\040', '\061', '\012', '\172', '\123', '\152', '\040', + '\163', '\172', '\040', '\061', '\012', '\166', '\150', '\121', + '\040', '\164', '\150', '\040', '\061', '\012', '\164', '\172', + '\130', '\040', '\164', '\150', '\040', '\061', '\012', '\107', + '\166', '\155', '\040', '\166', '\141', '\040', '\061', '\012', + '\143', '\161', '\125', '\040', '\143', '\150', '\040', '\061', + '\012', '\110', '\150', '\160', '\040', '\164', '\150', '\040', + '\061', '\012', '\147', '\121', '\153', '\040', '\156', '\147', + '\040', '\061', '\012', '\160', '\167', '\114', '\040', '\160', + '\162', '\040', '\061', '\012', '\163', '\116', '\167', '\040', + '\163', '\164', '\040', '\061', '\012', '\161', '\105', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\116', '\172', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\172', + '\163', '\104', '\040', '\163', '\164', '\040', '\061', '\012', + '\155', '\104', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\122', '\164', '\161', '\040', '\164', '\150', '\040', + '\061', '\012', '\152', '\114', '\146', '\040', '\151', '\152', + '\040', '\061', '\012', '\167', '\124', '\160', '\040', '\160', + '\162', '\040', '\061', '\012', '\170', '\112', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\126', '\161', '\157', + '\040', '\161', '\165', '\040', '\061', '\012', '\132', '\161', + '\153', '\040', '\161', '\165', '\040', '\061', '\012', '\161', + '\161', '\121', '\040', '\161', '\165', '\040', '\061', '\012', + '\150', '\162', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\127', '\161', '\157', '\040', '\161', '\165', '\040', + '\061', '\012', '\155', '\111', '\171', '\040', '\155', '\145', + '\040', '\061', '\012', '\111', '\160', '\153', '\040', '\153', + '\141', '\040', '\061', '\012', '\170', '\152', '\103', '\040', + '\151', '\152', '\040', '\061', '\012', '\154', '\114', '\160', + '\040', '\154', '\145', '\040', '\061', '\012', '\150', '\161', + '\106', '\040', '\164', '\150', '\040', '\061', '\012', '\143', + '\127', '\147', '\040', '\143', '\150', '\040', '\061', '\012', + '\161', '\131', '\143', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\152', '\125', '\040', '\143', '\150', '\040', + '\061', '\012', '\161', '\130', '\153', '\040', '\161', '\165', + '\040', '\061', '\012', '\150', '\161', '\114', '\040', '\164', + '\150', '\040', '\061', '\012', '\172', '\170', '\124', '\040', + '\163', '\172', '\040', '\061', '\012', '\144', '\156', '\130', + '\040', '\141', '\156', '\040', '\061', '\012', '\172', '\102', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\121', + '\154', '\163', '\040', '\154', '\145', '\040', '\061', '\012', + '\153', '\150', '\103', '\040', '\164', '\150', '\040', '\061', + '\012', '\165', '\161', '\130', '\040', '\161', '\165', '\040', + '\061', '\012', '\132', '\142', '\146', '\040', '\142', '\145', + '\040', '\061', '\012', '\151', '\104', '\170', '\040', '\154', + '\151', '\040', '\061', '\012', '\132', '\156', '\160', '\040', + '\141', '\156', '\040', '\061', '\012', '\112', '\170', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\152', '\161', + '\131', '\040', '\161', '\165', '\040', '\061', '\012', '\166', + '\142', '\125', '\040', '\166', '\141', '\040', '\061', '\012', + '\161', '\122', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\160', '\152', '\040', '\161', '\165', '\040', + '\061', '\012', '\167', '\154', '\107', '\040', '\154', '\145', + '\040', '\061', '\012', '\127', '\147', '\170', '\040', '\156', + '\147', '\040', '\061', '\012', '\126', '\170', '\152', '\040', + '\151', '\152', '\040', '\061', '\012', '\172', '\123', '\167', + '\040', '\163', '\172', '\040', '\061', '\012', '\151', '\150', + '\127', '\040', '\164', '\150', '\040', '\061', '\012', '\153', + '\172', '\124', '\040', '\163', '\172', '\040', '\061', '\012', + '\141', '\145', '\132', '\040', '\141', '\156', '\040', '\061', + '\012', '\150', '\113', '\152', '\040', '\164', '\150', '\040', + '\061', '\012', '\164', '\127', '\163', '\040', '\164', '\150', + '\040', '\061', '\012', '\147', '\114', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\147', '\160', '\113', '\040', + '\156', '\147', '\040', '\061', '\012', '\171', '\112', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\107', '\166', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\146', + '\105', '\157', '\040', '\157', '\156', '\040', '\061', '\012', + '\163', '\113', '\144', '\040', '\163', '\164', '\040', '\061', + '\012', '\170', '\150', '\116', '\040', '\164', '\150', '\040', + '\061', '\012', '\141', '\115', '\161', '\040', '\141', '\156', + '\040', '\061', '\012', '\145', '\150', '\130', '\040', '\164', + '\150', '\040', '\061', '\012', '\153', '\146', '\132', '\040', + '\153', '\165', '\040', '\061', '\012', '\127', '\167', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\131', '\155', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\126', + '\153', '\144', '\040', '\144', '\145', '\040', '\061', '\012', + '\142', '\172', '\104', '\040', '\163', '\172', '\040', '\061', + '\012', '\130', '\153', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\126', '\172', '\172', '\040', '\163', '\172', + '\040', '\061', '\012', '\170', '\166', '\126', '\040', '\166', + '\141', '\040', '\061', '\012', '\160', '\110', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\162', '\113', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\166', '\155', + '\115', '\040', '\166', '\141', '\040', '\061', '\012', '\121', + '\170', '\152', '\040', '\151', '\152', '\040', '\061', '\012', + '\172', '\116', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\142', '\161', '\102', '\040', '\161', '\165', '\040', + '\061', '\012', '\112', '\161', '\167', '\040', '\161', '\165', + '\040', '\061', '\012', '\172', '\161', '\102', '\040', '\161', + '\165', '\040', '\061', '\012', '\130', '\166', '\155', '\040', + '\166', '\141', '\040', '\061', '\012', '\154', '\102', '\146', + '\040', '\154', '\145', '\040', '\061', '\012', '\161', '\161', + '\102', '\040', '\161', '\165', '\040', '\061', '\012', '\147', + '\103', '\163', '\040', '\156', '\147', '\040', '\061', '\012', + '\162', '\122', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\122', '\156', '\155', '\040', '\141', '\156', '\040', + '\061', '\012', '\114', '\172', '\167', '\040', '\163', '\172', + '\040', '\061', '\012', '\151', '\167', '\116', '\040', '\151', + '\156', '\040', '\061', '\012', '\160', '\146', '\116', '\040', + '\160', '\162', '\040', '\061', '\012', '\150', '\103', '\167', + '\040', '\167', '\141', '\040', '\061', '\012', '\165', '\110', + '\172', '\040', '\161', '\165', '\040', '\061', '\012', '\143', + '\114', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\154', '\167', '\104', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\152', '\102', '\040', '\161', '\165', '\040', + '\061', '\012', '\117', '\152', '\171', '\040', '\151', '\152', + '\040', '\061', '\012', '\144', '\155', '\126', '\040', '\144', + '\151', '\040', '\061', '\012', '\143', '\103', '\167', '\040', + '\143', '\150', '\040', '\061', '\012', '\154', '\130', '\163', + '\040', '\154', '\145', '\040', '\061', '\012', '\163', '\155', + '\122', '\040', '\163', '\164', '\040', '\061', '\012', '\155', + '\170', '\117', '\040', '\155', '\145', '\040', '\061', '\012', + '\112', '\162', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\152', '\116', '\040', '\163', '\172', '\040', + '\061', '\012', '\142', '\102', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\143', '\170', '\121', '\040', '\143', + '\150', '\040', '\061', '\012', '\113', '\144', '\160', '\040', + '\144', '\145', '\040', '\061', '\012', '\104', '\154', '\142', + '\040', '\154', '\145', '\040', '\061', '\012', '\160', '\161', + '\104', '\040', '\161', '\165', '\040', '\061', '\012', '\161', + '\161', '\103', '\040', '\161', '\165', '\040', '\061', '\012', + '\123', '\160', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\164', '\103', '\144', '\040', '\164', '\150', '\040', + '\061', '\012', '\147', '\146', '\120', '\040', '\156', '\147', + '\040', '\061', '\012', '\165', '\107', '\152', '\040', '\161', + '\165', '\040', '\061', '\012', '\170', '\142', '\105', '\040', + '\142', '\145', '\040', '\061', '\012', '\130', '\160', '\166', + '\040', '\166', '\141', '\040', '\061', '\012', '\130', '\172', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\147', + '\161', '\107', '\040', '\161', '\165', '\040', '\061', '\012', + '\153', '\161', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\113', '\166', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\161', '\127', '\151', '\040', '\161', '\165', + '\040', '\061', '\012', '\155', '\170', '\132', '\040', '\155', + '\145', '\040', '\061', '\012', '\161', '\157', '\131', '\040', + '\161', '\165', '\040', '\061', '\012', '\123', '\147', '\146', + '\040', '\156', '\147', '\040', '\061', '\012', '\143', '\122', + '\166', '\040', '\143', '\150', '\040', '\061', '\012', '\127', + '\147', '\151', '\040', '\156', '\147', '\040', '\061', '\012', + '\145', '\104', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\143', '\127', '\167', '\040', '\143', '\150', '\040', + '\061', '\012', '\166', '\106', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\113', '\170', '\166', '\040', '\166', + '\141', '\040', '\061', '\012', '\151', '\127', '\160', '\040', + '\151', '\156', '\040', '\061', '\012', '\146', '\122', '\170', + '\040', '\146', '\157', '\040', '\061', '\012', '\167', '\164', + '\102', '\040', '\164', '\150', '\040', '\061', '\012', '\163', + '\167', '\127', '\040', '\163', '\164', '\040', '\061', '\012', + '\147', '\162', '\113', '\040', '\156', '\147', '\040', '\061', + '\012', '\110', '\146', '\145', '\040', '\145', '\162', '\040', + '\061', '\012', '\147', '\146', '\132', '\040', '\156', '\147', + '\040', '\061', '\012', '\170', '\161', '\130', '\040', '\161', + '\165', '\040', '\061', '\012', '\157', '\113', '\152', '\040', + '\157', '\156', '\040', '\061', '\012', '\166', '\146', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\160', '\127', + '\167', '\040', '\160', '\162', '\040', '\061', '\012', '\165', + '\127', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\154', '\103', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\153', '\147', '\040', '\161', '\165', '\040', + '\061', '\012', '\143', '\104', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\123', '\146', '\172', '\040', '\163', + '\172', '\040', '\061', '\012', '\165', '\131', '\170', '\040', + '\161', '\165', '\040', '\061', '\012', '\170', '\166', '\122', + '\040', '\166', '\141', '\040', '\061', '\012', '\145', '\101', + '\157', '\040', '\145', '\162', '\040', '\061', '\012', '\160', + '\131', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\144', '\122', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\151', '\127', '\144', '\040', '\151', '\156', '\040', + '\061', '\012', '\147', '\107', '\170', '\040', '\156', '\147', + '\040', '\061', '\012', '\142', '\130', '\172', '\040', '\163', + '\172', '\040', '\061', '\012', '\153', '\143', '\120', '\040', + '\143', '\150', '\040', '\061', '\012', '\150', '\143', '\112', + '\040', '\164', '\150', '\040', '\061', '\012', '\154', '\103', + '\146', '\040', '\154', '\145', '\040', '\061', '\012', '\147', + '\155', '\127', '\040', '\156', '\147', '\040', '\061', '\012', + '\110', '\153', '\146', '\040', '\153', '\141', '\040', '\061', + '\012', '\162', '\150', '\114', '\040', '\164', '\150', '\040', + '\061', '\012', '\152', '\161', '\120', '\040', '\161', '\165', + '\040', '\061', '\012', '\162', '\121', '\160', '\040', '\145', + '\162', '\040', '\061', '\012', '\166', '\103', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\144', '\127', '\152', + '\040', '\144', '\145', '\040', '\061', '\012', '\110', '\162', + '\170', '\040', '\145', '\162', '\040', '\061', '\012', '\163', + '\124', '\172', '\040', '\163', '\164', '\040', '\061', '\012', + '\141', '\126', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\167', '\113', '\040', '\161', '\165', '\040', + '\061', '\012', '\166', '\166', '\105', '\040', '\166', '\141', + '\040', '\061', '\012', '\167', '\113', '\160', '\040', '\160', + '\162', '\040', '\061', '\012', '\170', '\143', '\131', '\040', + '\143', '\150', '\040', '\061', '\012', '\166', '\160', '\115', + '\040', '\166', '\141', '\040', '\061', '\012', '\152', '\154', + '\103', '\040', '\154', '\145', '\040', '\061', '\012', '\144', + '\154', '\107', '\040', '\154', '\145', '\040', '\061', '\012', + '\157', '\124', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\151', '\114', '\160', '\040', '\151', '\156', '\040', + '\061', '\012', '\170', '\163', '\114', '\040', '\163', '\164', + '\040', '\061', '\012', '\154', '\106', '\172', '\040', '\154', + '\145', '\040', '\061', '\012', '\166', '\150', '\103', '\040', + '\164', '\150', '\040', '\061', '\012', '\171', '\154', '\130', + '\040', '\154', '\145', '\040', '\061', '\012', '\160', '\155', + '\117', '\040', '\155', '\145', '\040', '\061', '\012', '\131', + '\143', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\131', '\156', '\160', '\040', '\141', '\156', '\040', '\061', + '\012', '\131', '\142', '\155', '\040', '\155', '\145', '\040', + '\061', '\012', '\121', '\154', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\142', '\170', '\101', '\040', '\142', + '\145', '\040', '\061', '\012', '\164', '\106', '\163', '\040', + '\164', '\150', '\040', '\061', '\012', '\114', '\161', '\167', + '\040', '\161', '\165', '\040', '\061', '\012', '\172', '\143', + '\125', '\040', '\143', '\150', '\040', '\061', '\012', '\166', + '\146', '\113', '\040', '\166', '\141', '\040', '\061', '\012', + '\166', '\160', '\121', '\040', '\166', '\141', '\040', '\061', + '\012', '\104', '\164', '\146', '\040', '\164', '\150', '\040', + '\061', '\012', '\142', '\124', '\152', '\040', '\151', '\152', + '\040', '\061', '\012', '\126', '\166', '\167', '\040', '\166', + '\141', '\040', '\061', '\012', '\121', '\142', '\170', '\040', + '\142', '\145', '\040', '\061', '\012', '\172', '\127', '\153', + '\040', '\163', '\172', '\040', '\061', '\012', '\142', '\123', + '\170', '\040', '\142', '\145', '\040', '\061', '\012', '\172', + '\160', '\113', '\040', '\163', '\172', '\040', '\061', '\012', + '\167', '\124', '\142', '\040', '\167', '\141', '\040', '\061', + '\012', '\155', '\153', '\103', '\040', '\153', '\141', '\040', + '\061', '\012', '\143', '\122', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\156', '\102', '\153', '\040', '\141', + '\156', '\040', '\061', '\012', '\170', '\107', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\150', '\156', '\121', + '\040', '\164', '\150', '\040', '\061', '\012', '\141', '\161', + '\121', '\040', '\141', '\156', '\040', '\061', '\012', '\172', + '\150', '\132', '\040', '\164', '\150', '\040', '\061', '\012', + '\172', '\167', '\120', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\161', '\114', '\040', '\161', '\165', '\040', + '\061', '\012', '\163', '\143', '\125', '\040', '\143', '\150', + '\040', '\061', '\012', '\147', '\154', '\123', '\040', '\156', + '\147', '\040', '\061', '\012', '\160', '\152', '\105', '\040', + '\151', '\152', '\040', '\061', '\012', '\161', '\161', '\104', + '\040', '\161', '\165', '\040', '\061', '\012', '\154', '\122', + '\170', '\040', '\154', '\145', '\040', '\061', '\012', '\161', + '\126', '\162', '\040', '\161', '\165', '\040', '\061', '\012', + '\130', '\165', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\162', '\102', '\040', '\145', '\162', '\040', + '\061', '\012', '\121', '\171', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\123', '\147', '\170', '\040', '\156', + '\147', '\040', '\061', '\012', '\144', '\161', '\153', '\040', + '\161', '\165', '\040', '\061', '\012', '\142', '\131', '\152', + '\040', '\151', '\152', '\040', '\061', '\012', '\155', '\120', + '\170', '\040', '\155', '\145', '\040', '\061', '\012', '\106', + '\144', '\166', '\040', '\144', '\145', '\040', '\061', '\012', + '\130', '\155', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\143', '\120', '\152', '\040', '\143', '\150', '\040', + '\061', '\012', '\120', '\161', '\147', '\040', '\161', '\165', + '\040', '\061', '\012', '\166', '\131', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\142', '\112', '\170', '\040', + '\142', '\145', '\040', '\061', '\012', '\144', '\121', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\146', '\170', + '\152', '\040', '\151', '\152', '\040', '\061', '\012', '\110', + '\167', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\166', '\147', '\103', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\152', '\113', '\040', '\151', '\152', '\040', + '\061', '\012', '\156', '\162', '\103', '\040', '\141', '\156', + '\040', '\061', '\012', '\166', '\161', '\130', '\040', '\161', + '\165', '\040', '\061', '\012', '\102', '\147', '\153', '\040', + '\156', '\147', '\040', '\061', '\012', '\103', '\142', '\166', + '\040', '\166', '\141', '\040', '\061', '\012', '\125', '\167', + '\167', '\040', '\167', '\141', '\040', '\061', '\012', '\167', + '\143', '\112', '\040', '\143', '\150', '\040', '\061', '\012', + '\147', '\102', '\146', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\124', '\166', '\040', '\166', '\141', '\040', + '\061', '\012', '\172', '\167', '\130', '\040', '\163', '\172', + '\040', '\061', '\012', '\154', '\127', '\147', '\040', '\154', + '\145', '\040', '\061', '\012', '\161', '\117', '\163', '\040', + '\161', '\165', '\040', '\061', '\012', '\146', '\142', '\102', + '\040', '\142', '\145', '\040', '\061', '\012', '\170', '\161', + '\107', '\040', '\161', '\165', '\040', '\061', '\012', '\152', + '\121', '\152', '\040', '\151', '\152', '\040', '\061', '\012', + '\166', '\157', '\121', '\040', '\157', '\156', '\040', '\061', + '\012', '\171', '\152', '\127', '\040', '\151', '\152', '\040', + '\061', '\012', '\161', '\166', '\117', '\040', '\161', '\165', + '\040', '\061', '\012', '\170', '\142', '\106', '\040', '\142', + '\145', '\040', '\061', '\012', '\156', '\127', '\165', '\040', + '\141', '\156', '\040', '\061', '\012', '\171', '\152', '\121', + '\040', '\151', '\152', '\040', '\061', '\012', '\143', '\152', + '\113', '\040', '\143', '\150', '\040', '\061', '\012', '\123', + '\170', '\156', '\040', '\141', '\156', '\040', '\061', '\012', + '\171', '\142', '\130', '\040', '\142', '\145', '\040', '\061', + '\012', '\145', '\131', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\102', '\155', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\146', '\104', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\152', '\130', '\155', '\040', + '\151', '\152', '\040', '\061', '\012', '\156', '\115', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\123', '\170', + '\142', '\040', '\142', '\145', '\040', '\061', '\012', '\154', + '\110', '\155', '\040', '\154', '\145', '\040', '\061', '\012', + '\147', '\146', '\131', '\040', '\156', '\147', '\040', '\061', + '\012', '\156', '\167', '\107', '\040', '\141', '\156', '\040', + '\061', '\012', '\147', '\110', '\154', '\040', '\156', '\147', + '\040', '\061', '\012', '\127', '\160', '\155', '\040', '\155', + '\145', '\040', '\061', '\012', '\167', '\106', '\152', '\040', + '\151', '\152', '\040', '\061', '\012', '\150', '\107', '\155', + '\040', '\164', '\150', '\040', '\061', '\012', '\167', '\167', + '\103', '\040', '\167', '\141', '\040', '\061', '\012', '\115', + '\154', '\146', '\040', '\154', '\145', '\040', '\061', '\012', + '\143', '\112', '\142', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\156', '\103', '\040', '\141', '\156', '\040', + '\061', '\012', '\106', '\166', '\160', '\040', '\166', '\141', + '\040', '\061', '\012', '\164', '\107', '\143', '\040', '\164', + '\150', '\040', '\061', '\012', '\146', '\150', '\132', '\040', + '\164', '\150', '\040', '\061', '\012', '\126', '\153', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\152', '\167', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\170', + '\142', '\113', '\040', '\142', '\145', '\040', '\061', '\012', + '\172', '\126', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\124', '\172', '\040', '\161', '\165', '\040', + '\061', '\012', '\166', '\162', '\104', '\040', '\145', '\162', + '\040', '\061', '\012', '\146', '\122', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\146', '\106', '\163', '\040', + '\163', '\164', '\040', '\061', '\012', '\150', '\127', '\147', + '\040', '\164', '\150', '\040', '\061', '\012', '\154', '\172', + '\105', '\040', '\154', '\145', '\040', '\061', '\012', '\154', + '\167', '\130', '\040', '\154', '\145', '\040', '\061', '\012', + '\152', '\110', '\171', '\040', '\151', '\152', '\040', '\061', + '\012', '\121', '\161', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\104', '\161', '\151', '\040', '\151', '\156', + '\040', '\061', '\012', '\124', '\166', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\147', '\120', '\142', '\040', + '\156', '\147', '\040', '\061', '\012', '\144', '\120', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\172', '\144', + '\124', '\040', '\163', '\172', '\040', '\061', '\012', '\155', + '\166', '\101', '\040', '\166', '\141', '\040', '\061', '\012', + '\132', '\166', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\141', '\125', '\040', '\141', '\156', '\040', + '\061', '\012', '\146', '\167', '\121', '\040', '\167', '\141', + '\040', '\061', '\012', '\122', '\163', '\167', '\040', '\163', + '\164', '\040', '\061', '\012', '\153', '\154', '\102', '\040', + '\154', '\145', '\040', '\061', '\012', '\166', '\154', '\116', + '\040', '\154', '\145', '\040', '\061', '\012', '\107', '\166', + '\170', '\040', '\166', '\141', '\040', '\061', '\012', '\160', + '\144', '\112', '\040', '\144', '\145', '\040', '\061', '\012', + '\154', '\143', '\102', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\124', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\171', '\150', '\126', '\040', '\164', '\150', + '\040', '\061', '\012', '\152', '\114', '\166', '\040', '\151', + '\152', '\040', '\061', '\012', '\160', '\172', '\122', '\040', + '\163', '\172', '\040', '\061', '\012', '\130', '\171', '\167', + '\040', '\167', '\141', '\040', '\061', '\012', '\130', '\154', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\122', + '\161', '\167', '\040', '\167', '\141', '\040', '\061', '\012', + '\172', '\150', '\120', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\147', '\124', '\040', '\156', '\147', '\040', + '\061', '\012', '\147', '\160', '\107', '\040', '\156', '\147', + '\040', '\061', '\012', '\164', '\153', '\131', '\040', '\164', + '\150', '\040', '\061', '\012', '\144', '\161', '\105', '\040', + '\161', '\165', '\040', '\061', '\012', '\121', '\143', '\147', + '\040', '\143', '\150', '\040', '\061', '\012', '\142', '\146', + '\102', '\040', '\142', '\145', '\040', '\061', '\012', '\127', + '\160', '\166', '\040', '\166', '\141', '\040', '\061', '\012', + '\127', '\170', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\130', '\142', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\171', '\106', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\122', '\146', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\150', '\150', '\114', '\040', + '\164', '\150', '\040', '\061', '\012', '\152', '\170', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\142', '\113', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\160', + '\164', '\125', '\040', '\164', '\150', '\040', '\061', '\012', + '\143', '\130', '\145', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\130', '\155', '\040', '\163', '\172', '\040', + '\061', '\012', '\107', '\150', '\167', '\040', '\164', '\150', + '\040', '\061', '\012', '\144', '\172', '\131', '\040', '\163', + '\172', '\040', '\061', '\012', '\144', '\130', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\153', '\170', '\127', + '\040', '\153', '\141', '\040', '\061', '\012', '\166', '\126', + '\162', '\040', '\145', '\162', '\040', '\061', '\012', '\112', + '\170', '\165', '\040', '\165', '\156', '\040', '\061', '\012', + '\142', '\142', '\130', '\040', '\142', '\145', '\040', '\061', + '\012', '\162', '\120', '\142', '\040', '\145', '\162', '\040', + '\061', '\012', '\161', '\103', '\155', '\040', '\161', '\165', + '\040', '\061', '\012', '\161', '\151', '\112', '\040', '\161', + '\165', '\040', '\061', '\012', '\130', '\147', '\167', '\040', + '\156', '\147', '\040', '\061', '\012', '\116', '\150', '\161', + '\040', '\164', '\150', '\040', '\061', '\012', '\143', '\107', + '\160', '\040', '\160', '\157', '\040', '\061', '\012', '\150', + '\120', '\167', '\040', '\164', '\150', '\040', '\061', '\012', + '\142', '\124', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\111', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\160', '\112', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\167', '\143', '\105', '\040', '\143', + '\150', '\040', '\061', '\012', '\155', '\103', '\142', '\040', + '\155', '\145', '\040', '\061', '\012', '\142', '\112', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\156', '\172', + '\121', '\040', '\141', '\156', '\040', '\061', '\012', '\171', + '\161', '\122', '\040', '\161', '\165', '\040', '\061', '\012', + '\170', '\110', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\142', '\167', '\110', '\040', '\167', '\141', '\040', + '\061', '\012', '\161', '\103', '\162', '\040', '\161', '\165', + '\040', '\061', '\012', '\125', '\161', '\145', '\040', '\161', + '\165', '\040', '\061', '\012', '\161', '\170', '\115', '\040', + '\161', '\165', '\040', '\061', '\012', '\146', '\160', '\117', + '\040', '\160', '\162', '\040', '\061', '\012', '\153', '\143', + '\116', '\040', '\143', '\150', '\040', '\061', '\012', '\171', + '\153', '\126', '\040', '\153', '\141', '\040', '\061', '\012', + '\155', '\121', '\142', '\040', '\155', '\145', '\040', '\061', + '\012', '\131', '\161', '\163', '\040', '\161', '\165', '\040', + '\061', '\012', '\171', '\126', '\153', '\040', '\153', '\141', + '\040', '\061', '\012', '\166', '\142', '\130', '\040', '\166', + '\141', '\040', '\061', '\012', '\155', '\124', '\144', '\040', + '\144', '\145', '\040', '\061', '\012', '\152', '\130', '\157', + '\040', '\157', '\156', '\040', '\061', '\012', '\167', '\161', + '\112', '\040', '\161', '\165', '\040', '\061', '\012', '\153', + '\113', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\146', '\153', '\123', '\040', '\153', '\141', '\040', '\061', + '\012', '\127', '\166', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\111', '\171', '\166', '\040', '\166', '\141', + '\040', '\061', '\012', '\150', '\107', '\153', '\040', '\164', + '\150', '\040', '\061', '\012', '\106', '\172', '\145', '\040', + '\145', '\162', '\040', '\061', '\012', '\142', '\150', '\115', + '\040', '\164', '\150', '\040', '\061', '\012', '\161', '\166', + '\111', '\040', '\161', '\165', '\040', '\061', '\012', '\156', + '\130', '\161', '\040', '\141', '\156', '\040', '\061', '\012', + '\156', '\130', '\143', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\112', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\116', '\161', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\131', '\152', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\106', '\150', '\142', '\040', + '\164', '\150', '\040', '\061', '\012', '\152', '\171', '\113', + '\040', '\151', '\152', '\040', '\061', '\012', '\112', '\172', + '\152', '\040', '\163', '\172', '\040', '\061', '\012', '\171', + '\161', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\167', '\155', '\132', '\040', '\155', '\145', '\040', '\061', + '\012', '\172', '\142', '\106', '\040', '\163', '\172', '\040', + '\061', '\012', '\163', '\160', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\147', '\120', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\152', '\123', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\147', '\115', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\146', '\130', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\106', + '\171', '\167', '\040', '\167', '\141', '\040', '\061', '\012', + '\106', '\167', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\155', '\116', '\040', '\164', '\150', '\040', + '\061', '\012', '\150', '\116', '\154', '\040', '\164', '\150', + '\040', '\061', '\012', '\164', '\161', '\131', '\040', '\164', + '\150', '\040', '\061', '\012', '\160', '\107', '\155', '\040', + '\155', '\145', '\040', '\061', '\012', '\155', '\130', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\161', '\131', + '\171', '\040', '\161', '\165', '\040', '\061', '\012', '\122', + '\155', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\104', '\161', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\127', '\153', '\170', '\040', '\153', '\141', '\040', + '\061', '\012', '\144', '\160', '\124', '\040', '\144', '\145', + '\040', '\061', '\012', '\152', '\171', '\112', '\040', '\151', + '\152', '\040', '\061', '\012', '\112', '\161', '\152', '\040', + '\161', '\165', '\040', '\061', '\012', '\167', '\152', '\132', + '\040', '\151', '\152', '\040', '\061', '\012', '\170', '\116', + '\162', '\040', '\145', '\162', '\040', '\061', '\012', '\161', + '\101', '\155', '\040', '\161', '\165', '\040', '\061', '\012', + '\150', '\102', '\156', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\160', '\112', '\040', '\161', '\165', '\040', + '\061', '\012', '\171', '\147', '\127', '\040', '\156', '\147', + '\040', '\061', '\012', '\152', '\130', '\146', '\040', '\151', + '\152', '\040', '\061', '\012', '\162', '\115', '\154', '\040', + '\145', '\162', '\040', '\061', '\012', '\172', '\147', '\126', + '\040', '\156', '\147', '\040', '\061', '\012', '\156', '\114', + '\160', '\040', '\141', '\156', '\040', '\061', '\012', '\160', + '\106', '\170', '\040', '\160', '\162', '\040', '\061', '\012', + '\164', '\166', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\121', '\154', '\040', '\154', '\145', '\040', + '\061', '\012', '\146', '\144', '\106', '\040', '\144', '\145', + '\040', '\061', '\012', '\142', '\170', '\113', '\040', '\142', + '\145', '\040', '\061', '\012', '\102', '\143', '\170', '\040', + '\143', '\150', '\040', '\061', '\012', '\162', '\160', '\131', + '\040', '\145', '\162', '\040', '\061', '\012', '\163', '\112', + '\142', '\040', '\163', '\164', '\040', '\061', '\012', '\113', + '\166', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\153', '\116', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\110', '\144', '\040', '\163', '\172', '\040', + '\061', '\012', '\144', '\172', '\106', '\040', '\163', '\172', + '\040', '\061', '\012', '\164', '\112', '\161', '\040', '\164', + '\150', '\040', '\061', '\012', '\110', '\146', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\166', '\121', '\144', + '\040', '\144', '\145', '\040', '\061', '\012', '\160', '\113', + '\152', '\040', '\151', '\152', '\040', '\061', '\012', '\146', + '\150', '\126', '\040', '\164', '\150', '\040', '\061', '\012', + '\161', '\132', '\151', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\150', '\131', '\040', '\164', '\150', '\040', + '\061', '\012', '\166', '\161', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\164', '\156', '\121', '\040', '\164', + '\150', '\040', '\061', '\012', '\126', '\161', '\153', '\040', + '\161', '\165', '\040', '\061', '\012', '\172', '\112', '\146', + '\040', '\163', '\172', '\040', '\061', '\012', '\112', '\153', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\122', + '\167', '\146', '\040', '\167', '\141', '\040', '\061', '\012', + '\172', '\166', '\115', '\040', '\166', '\141', '\040', '\061', + '\012', '\142', '\170', '\131', '\040', '\142', '\145', '\040', + '\061', '\012', '\160', '\130', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\146', '\125', '\171', '\040', '\156', + '\171', '\040', '\061', '\012', '\160', '\166', '\105', '\040', + '\166', '\141', '\040', '\061', '\012', '\114', '\160', '\153', + '\040', '\153', '\141', '\040', '\061', '\012', '\144', '\172', + '\126', '\040', '\163', '\172', '\040', '\061', '\012', '\170', + '\111', '\146', '\040', '\146', '\157', '\040', '\061', '\012', + '\167', '\132', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\156', '\160', '\121', '\040', '\141', '\156', '\040', + '\061', '\012', '\160', '\127', '\153', '\040', '\153', '\141', + '\040', '\061', '\012', '\152', '\147', '\121', '\040', '\156', + '\147', '\040', '\061', '\012', '\112', '\161', '\162', '\040', + '\161', '\165', '\040', '\061', '\012', '\147', '\155', '\130', + '\040', '\156', '\147', '\040', '\061', '\012', '\152', '\146', + '\115', '\040', '\151', '\152', '\040', '\061', '\012', '\154', + '\127', '\152', '\040', '\154', '\145', '\040', '\061', '\012', + '\160', '\142', '\116', '\040', '\160', '\162', '\040', '\061', + '\012', '\146', '\166', '\106', '\040', '\166', '\141', '\040', + '\061', '\012', '\163', '\104', '\144', '\040', '\163', '\164', + '\040', '\061', '\012', '\161', '\144', '\102', '\040', '\161', + '\165', '\040', '\061', '\012', '\146', '\162', '\114', '\040', + '\145', '\162', '\040', '\061', '\012', '\165', '\110', '\156', + '\040', '\141', '\156', '\040', '\061', '\012', '\147', '\167', + '\116', '\040', '\156', '\147', '\040', '\061', '\012', '\171', + '\102', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\132', '\172', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\104', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\121', '\143', '\172', '\040', '\143', '\150', + '\040', '\061', '\012', '\161', '\172', '\146', '\040', '\161', + '\165', '\040', '\061', '\012', '\167', '\105', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\160', '\170', '\110', + '\040', '\160', '\162', '\040', '\061', '\012', '\146', '\161', + '\117', '\040', '\161', '\165', '\040', '\061', '\012', '\126', + '\161', '\145', '\040', '\161', '\165', '\040', '\061', '\012', + '\147', '\153', '\104', '\040', '\156', '\147', '\040', '\061', + '\012', '\130', '\146', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\165', '\130', '\147', '\040', '\161', '\165', + '\040', '\061', '\012', '\152', '\103', '\167', '\040', '\151', + '\152', '\040', '\061', '\012', '\120', '\172', '\165', '\040', + '\161', '\165', '\040', '\061', '\012', '\147', '\122', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\166', '\161', + '\110', '\040', '\161', '\165', '\040', '\061', '\012', '\166', + '\166', '\127', '\040', '\166', '\141', '\040', '\061', '\012', + '\122', '\146', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\147', '\161', '\112', '\040', '\161', '\165', '\040', + '\061', '\012', '\164', '\147', '\117', '\040', '\164', '\150', + '\040', '\061', '\012', '\167', '\125', '\171', '\040', '\167', + '\141', '\040', '\061', '\012', '\112', '\153', '\167', '\040', + '\153', '\141', '\040', '\061', '\012', '\150', '\123', '\163', + '\040', '\164', '\150', '\040', '\061', '\012', '\147', '\153', + '\127', '\040', '\156', '\147', '\040', '\061', '\012', '\121', + '\147', '\171', '\040', '\156', '\147', '\040', '\061', '\012', + '\144', '\112', '\142', '\040', '\144', '\145', '\040', '\061', + '\012', '\160', '\162', '\106', '\040', '\145', '\162', '\040', + '\061', '\012', '\142', '\165', '\130', '\040', '\161', '\165', + '\040', '\061', '\012', '\143', '\126', '\147', '\040', '\143', + '\150', '\040', '\061', '\012', '\152', '\164', '\125', '\040', + '\164', '\150', '\040', '\061', '\012', '\146', '\104', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\131', '\147', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\113', + '\161', '\162', '\040', '\161', '\165', '\040', '\061', '\012', + '\125', '\171', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\154', '\112', '\153', '\040', '\154', '\145', '\040', + '\061', '\012', '\163', '\170', '\131', '\040', '\163', '\164', + '\040', '\061', '\012', '\170', '\146', '\131', '\040', '\146', + '\157', '\040', '\061', '\012', '\130', '\153', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\143', '\147', '\132', + '\040', '\143', '\150', '\040', '\061', '\012', '\143', '\171', + '\130', '\040', '\143', '\150', '\040', '\061', '\012', '\147', + '\142', '\106', '\040', '\156', '\147', '\040', '\061', '\012', + '\172', '\124', '\153', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\163', '\125', '\040', '\164', '\150', '\040', + '\061', '\012', '\164', '\154', '\127', '\040', '\164', '\150', + '\040', '\061', '\012', '\132', '\172', '\166', '\040', '\163', + '\172', '\040', '\061', '\012', '\153', '\161', '\105', '\040', + '\161', '\165', '\040', '\061', '\012', '\154', '\160', '\121', + '\040', '\160', '\157', '\040', '\061', '\012', '\161', '\112', + '\165', '\040', '\165', '\156', '\040', '\061', '\012', '\150', + '\131', '\151', '\040', '\164', '\150', '\040', '\061', '\012', + '\172', '\154', '\115', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\104', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\110', '\166', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\116', '\163', '\146', '\040', '\163', + '\164', '\040', '\061', '\012', '\142', '\112', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\146', '\116', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\153', '\121', + '\157', '\040', '\157', '\156', '\040', '\061', '\012', '\113', + '\161', '\160', '\040', '\161', '\165', '\040', '\061', '\012', + '\142', '\113', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\155', '\110', '\160', '\040', '\155', '\145', '\040', + '\061', '\012', '\125', '\171', '\152', '\040', '\151', '\152', + '\040', '\061', '\012', '\143', '\170', '\131', '\040', '\143', + '\150', '\040', '\061', '\012', '\171', '\111', '\145', '\040', + '\145', '\162', '\040', '\061', '\012', '\161', '\124', '\152', + '\040', '\161', '\165', '\040', '\061', '\012', '\167', '\146', + '\120', '\040', '\167', '\141', '\040', '\061', '\012', '\146', + '\170', '\111', '\040', '\146', '\157', '\040', '\061', '\012', + '\166', '\121', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\166', '\116', '\040', '\166', '\141', '\040', + '\061', '\012', '\160', '\167', '\116', '\040', '\160', '\162', + '\040', '\061', '\012', '\166', '\141', '\121', '\040', '\141', + '\156', '\040', '\061', '\012', '\155', '\170', '\121', '\040', + '\155', '\145', '\040', '\061', '\012', '\142', '\144', '\126', + '\040', '\144', '\145', '\040', '\061', '\012', '\103', '\147', + '\152', '\040', '\156', '\147', '\040', '\061', '\012', '\170', + '\152', '\172', '\040', '\163', '\172', '\040', '\061', '\012', + '\127', '\161', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\160', '\117', '\040', '\160', '\162', '\040', + '\061', '\012', '\167', '\157', '\121', '\040', '\157', '\156', + '\040', '\061', '\012', '\170', '\131', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\146', '\160', '\124', '\040', + '\160', '\162', '\040', '\061', '\012', '\154', '\116', '\160', + '\040', '\154', '\145', '\040', '\061', '\012', '\160', '\166', + '\130', '\040', '\166', '\141', '\040', '\061', '\012', '\160', + '\114', '\160', '\040', '\160', '\162', '\040', '\061', '\012', + '\113', '\163', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\162', '\127', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\151', '\125', '\171', '\040', '\151', '\156', + '\040', '\061', '\012', '\142', '\146', '\130', '\040', '\142', + '\145', '\040', '\061', '\012', '\170', '\163', '\126', '\040', + '\163', '\164', '\040', '\061', '\012', '\130', '\156', '\152', + '\040', '\141', '\156', '\040', '\061', '\012', '\144', '\155', + '\127', '\040', '\144', '\145', '\040', '\061', '\012', '\157', + '\121', '\167', '\040', '\157', '\156', '\040', '\061', '\012', + '\132', '\170', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\117', '\141', '\171', '\040', '\141', '\156', '\040', + '\061', '\012', '\160', '\152', '\107', '\040', '\151', '\152', + '\040', '\061', '\012', '\132', '\142', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\110', '\161', '\154', '\040', + '\161', '\165', '\040', '\061', '\012', '\132', '\170', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\152', '\127', + '\144', '\040', '\144', '\145', '\040', '\061', '\012', '\161', + '\125', '\160', '\040', '\161', '\165', '\040', '\061', '\012', + '\161', '\170', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\103', '\157', '\040', '\161', '\165', '\040', + '\061', '\012', '\131', '\146', '\144', '\040', '\144', '\145', + '\040', '\061', '\012', '\166', '\166', '\125', '\040', '\166', + '\141', '\040', '\061', '\012', '\166', '\111', '\153', '\040', + '\153', '\141', '\040', '\061', '\012', '\104', '\146', '\152', + '\040', '\151', '\152', '\040', '\061', '\012', '\132', '\155', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\103', + '\161', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\166', '\121', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\116', '\142', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\164', '\112', '\163', '\040', '\164', '\150', + '\040', '\061', '\012', '\106', '\150', '\170', '\040', '\164', + '\150', '\040', '\061', '\012', '\144', '\172', '\121', '\040', + '\163', '\172', '\040', '\061', '\012', '\172', '\131', '\152', + '\040', '\151', '\152', '\040', '\061', '\012', '\161', '\102', + '\167', '\040', '\161', '\165', '\040', '\061', '\012', '\166', + '\143', '\126', '\040', '\143', '\150', '\040', '\061', '\012', + '\147', '\107', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\126', '\167', '\040', '\151', '\156', '\040', + '\061', '\012', '\106', '\172', '\160', '\040', '\163', '\172', + '\040', '\061', '\012', '\142', '\152', '\110', '\040', '\151', + '\152', '\040', '\061', '\012', '\143', '\165', '\131', '\040', + '\143', '\150', '\040', '\061', '\012', '\152', '\167', '\123', + '\040', '\151', '\152', '\040', '\061', '\012', '\103', '\161', + '\160', '\040', '\161', '\165', '\040', '\061', '\012', '\171', + '\112', '\166', '\040', '\166', '\141', '\040', '\061', '\012', + '\153', '\144', '\112', '\040', '\144', '\145', '\040', '\061', + '\012', '\153', '\144', '\124', '\040', '\144', '\145', '\040', + '\061', '\012', '\156', '\161', '\102', '\040', '\141', '\156', + '\040', '\061', '\012', '\150', '\127', '\163', '\040', '\164', + '\150', '\040', '\061', '\012', '\161', '\163', '\152', '\040', + '\161', '\165', '\040', '\061', '\012', '\150', '\114', '\167', + '\040', '\164', '\150', '\040', '\061', '\012', '\150', '\144', + '\130', '\040', '\164', '\150', '\040', '\061', '\012', '\143', + '\147', '\126', '\040', '\143', '\150', '\040', '\061', '\012', + '\164', '\131', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\145', '\132', '\170', '\040', '\145', '\162', '\040', + '\061', '\012', '\150', '\146', '\116', '\040', '\164', '\150', + '\040', '\061', '\012', '\147', '\166', '\167', '\040', '\156', + '\147', '\040', '\061', '\012', '\141', '\126', '\160', '\040', + '\141', '\156', '\040', '\061', '\012', '\147', '\115', '\163', + '\040', '\156', '\147', '\040', '\061', '\012', '\120', '\142', + '\146', '\040', '\142', '\145', '\040', '\061', '\012', '\155', + '\121', '\146', '\040', '\155', '\145', '\040', '\061', '\012', + '\171', '\125', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\166', '\107', '\146', '\040', '\166', '\141', '\040', + '\061', '\012', '\170', '\147', '\106', '\040', '\156', '\147', + '\040', '\061', '\012', '\172', '\166', '\131', '\040', '\163', + '\172', '\040', '\061', '\012', '\167', '\162', '\101', '\040', + '\145', '\162', '\040', '\061', '\012', '\171', '\162', '\115', + '\040', '\145', '\162', '\040', '\061', '\012', '\166', '\115', + '\152', '\040', '\151', '\152', '\040', '\061', '\012', '\125', + '\171', '\166', '\040', '\166', '\141', '\040', '\061', '\012', + '\144', '\114', '\160', '\040', '\144', '\145', '\040', '\061', + '\012', '\107', '\152', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\172', '\105', '\151', '\040', '\151', '\156', + '\040', '\061', '\012', '\130', '\144', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\152', '\110', '\146', '\040', + '\151', '\152', '\040', '\061', '\012', '\157', '\120', '\172', + '\040', '\157', '\156', '\040', '\061', '\012', '\170', '\111', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\142', + '\103', '\142', '\040', '\142', '\145', '\040', '\061', '\012', + '\104', '\172', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\152', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\147', '\107', '\172', '\040', '\156', '\147', + '\040', '\061', '\012', '\155', '\152', '\125', '\040', '\151', + '\152', '\040', '\061', '\012', '\103', '\152', '\170', '\040', + '\151', '\152', '\040', '\061', '\012', '\170', '\113', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\155', '\166', + '\117', '\040', '\166', '\141', '\040', '\061', '\012', '\120', + '\172', '\142', '\040', '\163', '\172', '\040', '\061', '\012', + '\143', '\162', '\113', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\150', '\117', '\040', '\164', '\150', '\040', + '\061', '\012', '\171', '\154', '\102', '\040', '\154', '\145', + '\040', '\061', '\012', '\154', '\104', '\153', '\040', '\154', + '\145', '\040', '\061', '\012', '\172', '\154', '\117', '\040', + '\154', '\145', '\040', '\061', '\012', '\160', '\147', '\110', + '\040', '\156', '\147', '\040', '\061', '\012', '\166', '\121', + '\142', '\040', '\166', '\141', '\040', '\061', '\012', '\163', + '\144', '\132', '\040', '\163', '\164', '\040', '\061', '\012', + '\153', '\121', '\155', '\040', '\153', '\141', '\040', '\061', + '\012', '\154', '\122', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\157', '\121', '\171', '\040', '\157', '\156', + '\040', '\061', '\012', '\164', '\167', '\103', '\040', '\164', + '\150', '\040', '\061', '\012', '\102', '\144', '\152', '\040', + '\151', '\152', '\040', '\061', '\012', '\121', '\152', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\144', '\156', + '\120', '\040', '\141', '\156', '\040', '\061', '\012', '\116', + '\156', '\160', '\040', '\141', '\156', '\040', '\061', '\012', + '\161', '\151', '\120', '\040', '\161', '\165', '\040', '\061', + '\012', '\103', '\143', '\152', '\040', '\143', '\150', '\040', + '\061', '\012', '\165', '\110', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\161', '\114', '\170', '\040', '\161', + '\165', '\040', '\061', '\012', '\121', '\163', '\146', '\040', + '\163', '\164', '\040', '\061', '\012', '\146', '\113', '\170', + '\040', '\146', '\157', '\040', '\061', '\012', '\146', '\153', + '\105', '\040', '\153', '\141', '\040', '\061', '\012', '\152', + '\154', '\130', '\040', '\154', '\145', '\040', '\061', '\012', + '\152', '\132', '\142', '\040', '\151', '\152', '\040', '\061', + '\012', '\126', '\167', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\172', '\142', '\101', '\040', '\163', '\172', + '\040', '\061', '\012', '\110', '\150', '\144', '\040', '\164', + '\150', '\040', '\061', '\012', '\143', '\142', '\131', '\040', + '\143', '\150', '\040', '\061', '\012', '\111', '\153', '\146', + '\040', '\153', '\141', '\040', '\061', '\012', '\107', '\162', + '\170', '\040', '\145', '\162', '\040', '\061', '\012', '\152', + '\160', '\120', '\040', '\151', '\152', '\040', '\061', '\012', + '\121', '\146', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\150', '\127', '\040', '\164', '\150', '\040', + '\061', '\012', '\167', '\155', '\130', '\040', '\155', '\145', + '\040', '\061', '\012', '\141', '\112', '\142', '\040', '\141', + '\156', '\040', '\061', '\012', '\163', '\146', '\117', '\040', + '\163', '\164', '\040', '\061', '\012', '\161', '\130', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\155', '\130', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\142', + '\156', '\126', '\040', '\141', '\156', '\040', '\061', '\012', + '\131', '\160', '\167', '\040', '\160', '\162', '\040', '\061', + '\012', '\172', '\103', '\171', '\040', '\163', '\172', '\040', + '\061', '\012', '\154', '\150', '\116', '\040', '\164', '\150', + '\040', '\061', '\012', '\162', '\130', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\146', '\107', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\127', '\170', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\143', '\170', + '\124', '\040', '\143', '\150', '\040', '\061', '\012', '\132', + '\163', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\165', '\107', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\172', '\115', '\040', '\163', '\172', '\040', + '\061', '\012', '\172', '\152', '\123', '\040', '\163', '\172', + '\040', '\061', '\012', '\144', '\146', '\123', '\040', '\144', + '\145', '\040', '\061', '\012', '\147', '\160', '\110', '\040', + '\156', '\147', '\040', '\061', '\012', '\161', '\147', '\117', + '\040', '\156', '\147', '\040', '\061', '\012', '\153', '\161', + '\106', '\040', '\161', '\165', '\040', '\061', '\012', '\161', + '\146', '\125', '\040', '\161', '\165', '\040', '\061', '\012', + '\161', '\124', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\132', '\142', '\040', '\166', '\141', '\040', + '\061', '\012', '\105', '\152', '\167', '\040', '\151', '\152', + '\040', '\061', '\012', '\172', '\121', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\147', '\131', '\172', '\040', + '\156', '\147', '\040', '\061', '\012', '\153', '\152', '\126', + '\040', '\151', '\152', '\040', '\061', '\012', '\146', '\127', + '\154', '\040', '\154', '\145', '\040', '\061', '\012', '\146', + '\122', '\153', '\040', '\153', '\141', '\040', '\061', '\012', + '\165', '\123', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\103', '\170', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\114', '\143', '\166', '\040', '\143', '\150', + '\040', '\061', '\012', '\142', '\172', '\113', '\040', '\163', + '\172', '\040', '\061', '\012', '\167', '\161', '\106', '\040', + '\161', '\165', '\040', '\061', '\012', '\161', '\112', '\160', + '\040', '\161', '\165', '\040', '\061', '\012', '\162', '\103', + '\152', '\040', '\145', '\162', '\040', '\061', '\012', '\161', + '\166', '\163', '\040', '\161', '\165', '\040', '\061', '\012', + '\154', '\167', '\116', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\155', '\122', '\040', '\155', '\145', '\040', + '\061', '\012', '\142', '\164', '\103', '\040', '\164', '\150', + '\040', '\061', '\012', '\153', '\124', '\170', '\040', '\153', + '\141', '\040', '\061', '\012', '\161', '\153', '\125', '\040', + '\161', '\165', '\040', '\061', '\012', '\114', '\150', '\152', + '\040', '\164', '\150', '\040', '\061', '\012', '\144', '\111', + '\170', '\040', '\144', '\145', '\040', '\061', '\012', '\166', + '\163', '\121', '\040', '\163', '\164', '\040', '\061', '\012', + '\147', '\123', '\144', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\104', '\154', '\040', '\154', '\145', '\040', + '\061', '\012', '\126', '\152', '\155', '\040', '\151', '\152', + '\040', '\061', '\012', '\160', '\155', '\111', '\040', '\155', + '\145', '\040', '\061', '\012', '\166', '\127', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\146', '\113', '\166', + '\040', '\166', '\141', '\040', '\061', '\012', '\170', '\120', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\165', + '\157', '\121', '\040', '\161', '\165', '\040', '\061', '\012', + '\113', '\147', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\167', '\130', '\040', '\156', '\147', '\040', + '\061', '\012', '\163', '\147', '\112', '\040', '\156', '\147', + '\040', '\061', '\012', '\160', '\127', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\121', '\146', '\146', '\040', + '\146', '\157', '\040', '\061', '\012', '\150', '\153', '\112', + '\040', '\164', '\150', '\040', '\061', '\012', '\110', '\161', + '\157', '\040', '\161', '\165', '\040', '\061', '\012', '\152', + '\167', '\127', '\040', '\151', '\152', '\040', '\061', '\012', + '\163', '\121', '\172', '\040', '\163', '\164', '\040', '\061', + '\012', '\167', '\125', '\167', '\040', '\167', '\141', '\040', + '\061', '\012', '\155', '\113', '\170', '\040', '\155', '\145', + '\040', '\061', '\012', '\157', '\121', '\146', '\040', '\157', + '\156', '\040', '\061', '\012', '\152', '\126', '\153', '\040', + '\151', '\152', '\040', '\061', '\012', '\170', '\167', '\124', + '\040', '\167', '\141', '\040', '\061', '\012', '\163', '\124', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\165', + '\161', '\126', '\040', '\161', '\165', '\040', '\061', '\012', + '\121', '\154', '\160', '\040', '\154', '\145', '\040', '\061', + '\012', '\160', '\115', '\142', '\040', '\160', '\162', '\040', + '\061', '\012', '\170', '\113', '\152', '\040', '\151', '\152', + '\040', '\061', '\012', '\142', '\160', '\130', '\040', '\160', + '\162', '\040', '\061', '\012', '\166', '\121', '\145', '\040', + '\145', '\162', '\040', '\061', '\012', '\112', '\152', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\161', '\113', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\146', + '\153', '\112', '\040', '\153', '\141', '\040', '\061', '\012', + '\152', '\142', '\121', '\040', '\151', '\152', '\040', '\061', + '\012', '\155', '\132', '\167', '\040', '\155', '\145', '\040', + '\061', '\012', '\130', '\147', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\166', '\172', '\125', '\040', '\163', + '\172', '\040', '\061', '\012', '\160', '\124', '\155', '\040', + '\155', '\145', '\040', '\061', '\012', '\160', '\116', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\162', '\167', + '\104', '\040', '\145', '\162', '\040', '\061', '\012', '\121', + '\144', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\167', '\161', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\162', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\161', '\167', '\167', '\040', '\161', '\165', + '\040', '\061', '\012', '\161', '\167', '\125', '\040', '\161', + '\165', '\040', '\061', '\012', '\170', '\172', '\106', '\040', + '\163', '\172', '\040', '\061', '\012', '\146', '\154', '\127', + '\040', '\154', '\145', '\040', '\061', '\012', '\152', '\172', + '\120', '\040', '\163', '\172', '\040', '\061', '\012', '\127', + '\170', '\160', '\040', '\160', '\162', '\040', '\061', '\012', + '\162', '\104', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\107', '\160', '\040', '\144', '\145', '\040', + '\061', '\012', '\132', '\164', '\152', '\040', '\164', '\150', + '\040', '\061', '\012', '\125', '\166', '\160', '\040', '\166', + '\141', '\040', '\061', '\012', '\145', '\107', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\172', '\132', '\142', + '\040', '\163', '\172', '\040', '\061', '\012', '\147', '\121', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\164', + '\106', '\144', '\040', '\164', '\150', '\040', '\061', '\012', + '\115', '\161', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\156', '\104', '\040', '\141', '\156', '\040', + '\061', '\012', '\150', '\166', '\131', '\040', '\164', '\150', + '\040', '\061', '\012', '\111', '\171', '\142', '\040', '\142', + '\145', '\040', '\061', '\012', '\146', '\104', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\113', '\142', '\152', + '\040', '\151', '\152', '\040', '\061', '\012', '\166', '\131', + '\155', '\040', '\166', '\141', '\040', '\061', '\012', '\127', + '\170', '\162', '\040', '\145', '\162', '\040', '\061', '\012', + '\113', '\167', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\162', '\121', '\040', '\164', '\150', '\040', + '\061', '\012', '\171', '\103', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\110', '\170', '\167', '\040', '\167', + '\141', '\040', '\061', '\012', '\150', '\105', '\146', '\040', + '\164', '\150', '\040', '\061', '\012', '\142', '\144', '\125', + '\040', '\144', '\145', '\040', '\061', '\012', '\163', '\107', + '\152', '\040', '\163', '\164', '\040', '\061', '\012', '\107', + '\167', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\142', '\131', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\155', '\125', '\040', '\163', '\172', '\040', + '\061', '\012', '\160', '\104', '\155', '\040', '\160', '\157', + '\040', '\061', '\012', '\161', '\155', '\103', '\040', '\161', + '\165', '\040', '\061', '\012', '\144', '\124', '\144', '\040', + '\144', '\145', '\040', '\061', '\012', '\121', '\170', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\165', '\126', + '\146', '\040', '\161', '\165', '\040', '\061', '\012', '\161', + '\101', '\154', '\040', '\161', '\165', '\040', '\061', '\012', + '\152', '\105', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\113', '\160', '\171', '\040', '\160', '\162', '\040', + '\061', '\012', '\110', '\161', '\166', '\040', '\161', '\165', + '\040', '\061', '\012', '\146', '\103', '\153', '\040', '\153', + '\141', '\040', '\061', '\012', '\141', '\161', '\132', '\040', + '\141', '\156', '\040', '\061', '\012', '\154', '\125', '\157', + '\040', '\157', '\156', '\040', '\061', '\012', '\120', '\166', + '\157', '\040', '\157', '\156', '\040', '\061', '\012', '\104', + '\161', '\146', '\040', '\161', '\165', '\040', '\061', '\012', + '\147', '\144', '\115', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\172', '\114', '\040', '\163', '\172', '\040', + '\061', '\012', '\102', '\150', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\144', '\107', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\167', '\164', '\131', '\040', + '\164', '\150', '\040', '\061', '\012', '\161', '\124', '\171', + '\040', '\161', '\165', '\040', '\061', '\012', '\125', '\170', + '\162', '\040', '\145', '\162', '\040', '\061', '\012', '\126', + '\166', '\155', '\040', '\166', '\141', '\040', '\061', '\012', + '\166', '\110', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\132', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\146', '\150', '\103', '\040', '\164', '\150', + '\040', '\061', '\012', '\170', '\144', '\132', '\040', '\144', + '\145', '\040', '\061', '\012', '\150', '\132', '\160', '\040', + '\164', '\150', '\040', '\061', '\012', '\120', '\155', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\143', '\146', + '\124', '\040', '\143', '\150', '\040', '\061', '\012', '\160', + '\152', '\111', '\040', '\151', '\152', '\040', '\061', '\012', + '\155', '\144', '\132', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\153', '\121', '\040', '\151', '\152', '\040', + '\061', '\012', '\123', '\144', '\152', '\040', '\144', '\145', + '\040', '\061', '\012', '\150', '\104', '\146', '\040', '\164', + '\150', '\040', '\061', '\012', '\145', '\112', '\152', '\040', + '\145', '\162', '\040', '\061', '\012', '\167', '\152', '\131', + '\040', '\151', '\152', '\040', '\061', '\012', '\172', '\114', + '\155', '\040', '\163', '\172', '\040', '\061', '\012', '\145', + '\106', '\163', '\040', '\145', '\162', '\040', '\061', '\012', + '\167', '\147', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\132', '\155', '\153', '\040', '\153', '\141', '\040', + '\061', '\012', '\154', '\166', '\112', '\040', '\154', '\145', + '\040', '\061', '\012', '\170', '\131', '\155', '\040', '\155', + '\145', '\040', '\061', '\012', '\116', '\172', '\146', '\040', + '\163', '\172', '\040', '\061', '\012', '\167', '\112', '\151', + '\040', '\151', '\156', '\040', '\061', '\012', '\171', '\121', + '\163', '\040', '\163', '\164', '\040', '\061', '\012', '\160', + '\146', '\115', '\040', '\160', '\162', '\040', '\061', '\012', + '\144', '\150', '\122', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\155', '\113', '\040', '\143', '\150', '\040', + '\061', '\012', '\144', '\150', '\115', '\040', '\164', '\150', + '\040', '\061', '\012', '\161', '\107', '\142', '\040', '\161', + '\165', '\040', '\061', '\012', '\167', '\166', '\121', '\040', + '\166', '\141', '\040', '\061', '\012', '\103', '\147', '\161', + '\040', '\156', '\147', '\040', '\061', '\012', '\112', '\146', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\142', + '\153', '\104', '\040', '\153', '\141', '\040', '\061', '\012', + '\146', '\144', '\123', '\040', '\144', '\145', '\040', '\061', + '\012', '\111', '\166', '\160', '\040', '\166', '\141', '\040', + '\061', '\012', '\107', '\153', '\152', '\040', '\151', '\152', + '\040', '\061', '\012', '\172', '\111', '\166', '\040', '\163', + '\172', '\040', '\061', '\012', '\102', '\172', '\154', '\040', + '\154', '\145', '\040', '\061', '\012', '\147', '\102', '\142', + '\040', '\156', '\147', '\040', '\061', '\012', '\124', '\160', + '\152', '\040', '\151', '\152', '\040', '\061', '\012', '\166', + '\171', '\131', '\040', '\166', '\141', '\040', '\061', '\012', + '\125', '\170', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\153', '\167', '\127', '\040', '\153', '\141', '\040', + '\061', '\012', '\147', '\120', '\146', '\040', '\156', '\147', + '\040', '\061', '\012', '\160', '\161', '\103', '\040', '\161', + '\165', '\040', '\061', '\012', '\143', '\124', '\152', '\040', + '\143', '\150', '\040', '\061', '\012', '\171', '\172', '\111', + '\040', '\163', '\172', '\040', '\061', '\012', '\131', '\160', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\142', + '\166', '\104', '\040', '\166', '\141', '\040', '\061', '\012', + '\170', '\103', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\143', '\121', '\040', '\143', '\150', '\040', + '\061', '\012', '\146', '\132', '\167', '\040', '\167', '\141', + '\040', '\061', '\012', '\132', '\170', '\146', '\040', '\146', + '\157', '\040', '\061', '\012', '\167', '\142', '\101', '\040', + '\167', '\141', '\040', '\061', '\012', '\142', '\124', '\146', + '\040', '\142', '\145', '\040', '\061', '\012', '\162', '\170', + '\122', '\040', '\145', '\162', '\040', '\061', '\012', '\161', + '\161', '\105', '\040', '\161', '\165', '\040', '\061', '\012', + '\171', '\106', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\160', '\116', '\146', '\040', '\160', '\162', '\040', + '\061', '\012', '\153', '\115', '\166', '\040', '\153', '\141', + '\040', '\061', '\012', '\166', '\125', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\167', '\117', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\150', '\170', '\110', + '\040', '\164', '\150', '\040', '\061', '\012', '\130', '\161', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\165', + '\111', '\165', '\040', '\161', '\165', '\040', '\061', '\012', + '\106', '\172', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\163', '\144', '\040', '\163', '\164', '\040', + '\061', '\012', '\157', '\152', '\131', '\040', '\157', '\156', + '\040', '\061', '\012', '\143', '\105', '\157', '\040', '\143', + '\150', '\040', '\061', '\012', '\154', '\167', '\122', '\040', + '\154', '\145', '\040', '\061', '\012', '\161', '\152', '\106', + '\040', '\161', '\165', '\040', '\061', '\012', '\152', '\124', + '\160', '\040', '\151', '\152', '\040', '\061', '\012', '\171', + '\172', '\124', '\040', '\163', '\172', '\040', '\061', '\012', + '\152', '\146', '\117', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\123', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\116', '\143', '\153', '\040', '\143', '\150', + '\040', '\061', '\012', '\150', '\167', '\106', '\040', '\164', + '\150', '\040', '\061', '\012', '\107', '\155', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\111', '\151', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\172', '\167', + '\105', '\040', '\163', '\172', '\040', '\061', '\012', '\161', + '\121', '\166', '\040', '\161', '\165', '\040', '\061', '\012', + '\170', '\126', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\131', '\167', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\163', '\106', '\170', '\040', '\163', '\164', + '\040', '\061', '\012', '\146', '\166', '\102', '\040', '\166', + '\141', '\040', '\061', '\012', '\161', '\131', '\145', '\040', + '\154', '\145', '\040', '\061', '\012', '\147', '\167', '\124', + '\040', '\156', '\147', '\040', '\061', '\012', '\127', '\152', + '\170', '\040', '\151', '\152', '\040', '\061', '\012', '\142', + '\110', '\156', '\040', '\141', '\156', '\040', '\061', '\012', + '\146', '\115', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\112', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\126', '\153', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\106', '\170', '\166', '\040', '\166', + '\141', '\040', '\061', '\012', '\154', '\110', '\166', '\040', + '\154', '\145', '\040', '\061', '\012', '\127', '\160', '\153', + '\040', '\153', '\141', '\040', '\061', '\012', '\170', '\101', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\162', + '\170', '\102', '\040', '\160', '\162', '\040', '\061', '\012', + '\170', '\165', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\111', '\142', '\040', '\160', '\162', '\040', + '\061', '\012', '\142', '\146', '\105', '\040', '\142', '\145', + '\040', '\061', '\012', '\147', '\122', '\170', '\040', '\156', + '\147', '\040', '\061', '\012', '\102', '\160', '\142', '\040', + '\160', '\162', '\040', '\061', '\012', '\142', '\170', '\116', + '\040', '\142', '\145', '\040', '\061', '\012', '\153', '\147', + '\125', '\040', '\156', '\147', '\040', '\061', '\012', '\120', + '\170', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\143', '\103', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\116', '\160', '\142', '\040', '\160', '\162', '\040', + '\061', '\012', '\154', '\170', '\105', '\040', '\154', '\145', + '\040', '\061', '\012', '\154', '\103', '\171', '\040', '\154', + '\145', '\040', '\061', '\012', '\144', '\147', '\130', '\040', + '\156', '\147', '\040', '\061', '\012', '\170', '\114', '\146', + '\040', '\146', '\157', '\040', '\061', '\012', '\142', '\121', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\161', + '\147', '\106', '\040', '\156', '\147', '\040', '\061', '\012', + '\160', '\170', '\132', '\040', '\160', '\162', '\040', '\061', + '\012', '\160', '\120', '\170', '\040', '\160', '\162', '\040', + '\061', '\012', '\151', '\131', '\172', '\040', '\151', '\156', + '\040', '\061', '\012', '\166', '\112', '\154', '\040', '\154', + '\145', '\040', '\061', '\012', '\153', '\124', '\146', '\040', + '\153', '\141', '\040', '\061', '\012', '\161', '\126', '\155', + '\040', '\161', '\165', '\040', '\061', '\012', '\147', '\167', + '\123', '\040', '\156', '\147', '\040', '\061', '\012', '\172', + '\124', '\144', '\040', '\163', '\172', '\040', '\061', '\012', + '\160', '\121', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\170', '\105', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\146', '\160', '\120', '\040', '\160', '\162', + '\040', '\061', '\012', '\161', '\152', '\167', '\040', '\161', + '\165', '\040', '\061', '\012', '\117', '\171', '\167', '\040', + '\167', '\141', '\040', '\061', '\012', '\155', '\143', '\117', + '\040', '\143', '\150', '\040', '\061', '\012', '\126', '\152', + '\144', '\040', '\144', '\145', '\040', '\061', '\012', '\161', + '\144', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\114', '\146', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\166', '\132', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\156', '\117', '\161', '\040', '\141', '\156', + '\040', '\061', '\012', '\161', '\152', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\163', '\113', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\167', '\147', '\125', + '\040', '\156', '\147', '\040', '\061', '\012', '\150', '\147', + '\130', '\040', '\164', '\150', '\040', '\061', '\012', '\144', + '\115', '\166', '\040', '\144', '\145', '\040', '\061', '\012', + '\130', '\143', '\160', '\040', '\143', '\150', '\040', '\061', + '\012', '\106', '\167', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\160', '\167', '\101', '\040', '\160', '\162', + '\040', '\061', '\012', '\114', '\160', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\142', '\153', '\120', '\040', + '\153', '\141', '\040', '\061', '\012', '\166', '\110', '\156', + '\040', '\141', '\156', '\040', '\061', '\012', '\112', '\152', + '\171', '\040', '\151', '\152', '\040', '\061', '\012', '\155', + '\103', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\167', '\166', '\115', '\040', '\166', '\141', '\040', '\061', + '\012', '\111', '\143', '\142', '\040', '\143', '\150', '\040', + '\061', '\012', '\153', '\146', '\112', '\040', '\153', '\141', + '\040', '\061', '\012', '\150', '\163', '\121', '\040', '\164', + '\150', '\040', '\061', '\012', '\144', '\127', '\144', '\040', + '\144', '\145', '\040', '\061', '\012', '\146', '\125', '\163', + '\040', '\163', '\164', '\040', '\061', '\012', '\146', '\114', + '\156', '\040', '\141', '\156', '\040', '\061', '\012', '\160', + '\152', '\116', '\040', '\151', '\152', '\040', '\061', '\012', + '\172', '\147', '\121', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\114', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\172', '\161', '\105', '\040', '\161', '\165', + '\040', '\061', '\012', '\121', '\155', '\166', '\040', '\166', + '\141', '\040', '\061', '\012', '\132', '\152', '\162', '\040', + '\145', '\162', '\040', '\061', '\012', '\132', '\153', '\160', + '\040', '\153', '\141', '\040', '\061', '\012', '\151', '\171', + '\110', '\040', '\151', '\156', '\040', '\061', '\012', '\167', + '\165', '\131', '\040', '\161', '\165', '\040', '\061', '\012', + '\155', '\172', '\124', '\040', '\163', '\172', '\040', '\061', + '\012', '\143', '\167', '\113', '\040', '\143', '\150', '\040', + '\061', '\012', '\142', '\103', '\155', '\040', '\155', '\145', + '\040', '\061', '\012', '\171', '\144', '\107', '\040', '\144', + '\145', '\040', '\061', '\012', '\170', '\144', '\125', '\040', + '\144', '\145', '\040', '\061', '\012', '\167', '\124', '\146', + '\040', '\167', '\141', '\040', '\061', '\012', '\154', '\110', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\161', + '\171', '\104', '\040', '\161', '\165', '\040', '\061', '\012', + '\170', '\154', '\126', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\171', '\124', '\040', '\161', '\165', '\040', + '\061', '\012', '\164', '\127', '\156', '\040', '\164', '\150', + '\040', '\061', '\012', '\162', '\115', '\172', '\040', '\145', + '\162', '\040', '\061', '\012', '\160', '\130', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\130', '\142', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\153', '\110', + '\155', '\040', '\153', '\141', '\040', '\061', '\012', '\143', + '\126', '\144', '\040', '\143', '\150', '\040', '\061', '\012', + '\161', '\172', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\144', '\116', '\040', '\144', '\145', '\040', + '\061', '\012', '\161', '\115', '\142', '\040', '\161', '\165', + '\040', '\061', '\012', '\171', '\152', '\123', '\040', '\151', + '\152', '\040', '\061', '\012', '\147', '\155', '\103', '\040', + '\156', '\147', '\040', '\061', '\012', '\172', '\111', '\151', + '\040', '\151', '\156', '\040', '\061', '\012', '\146', '\160', + '\115', '\040', '\160', '\162', '\040', '\061', '\012', '\154', + '\143', '\132', '\040', '\143', '\150', '\040', '\061', '\012', + '\161', '\110', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\112', '\152', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\152', '\154', '\107', '\040', '\154', '\145', + '\040', '\061', '\012', '\161', '\143', '\113', '\040', '\143', + '\150', '\040', '\061', '\012', '\170', '\121', '\155', '\040', + '\155', '\145', '\040', '\061', '\012', '\166', '\111', '\151', + '\040', '\151', '\156', '\040', '\061', '\012', '\167', '\102', + '\160', '\040', '\160', '\162', '\040', '\061', '\012', '\167', + '\143', '\111', '\040', '\143', '\150', '\040', '\061', '\012', + '\144', '\112', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\121', '\142', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\102', '\152', '\146', '\040', '\151', '\152', + '\040', '\061', '\012', '\144', '\160', '\131', '\040', '\144', + '\145', '\040', '\061', '\012', '\144', '\143', '\106', '\040', + '\143', '\150', '\040', '\061', '\012', '\170', '\123', '\152', + '\040', '\151', '\152', '\040', '\061', '\012', '\151', '\130', + '\152', '\040', '\151', '\156', '\040', '\061', '\012', '\121', + '\147', '\142', '\040', '\156', '\147', '\040', '\061', '\012', + '\147', '\104', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\170', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\170', '\143', '\121', '\040', '\143', '\150', + '\040', '\061', '\012', '\123', '\161', '\163', '\040', '\161', + '\165', '\040', '\061', '\012', '\121', '\155', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\147', '\143', '\125', + '\040', '\143', '\150', '\040', '\061', '\012', '\102', '\166', + '\166', '\040', '\166', '\141', '\040', '\061', '\012', '\160', + '\172', '\105', '\040', '\163', '\172', '\040', '\061', '\012', + '\167', '\164', '\124', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\142', '\114', '\040', '\166', '\141', '\040', + '\061', '\012', '\142', '\103', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\121', '\160', '\157', '\040', '\157', + '\156', '\040', '\061', '\012', '\155', '\130', '\163', '\040', + '\155', '\145', '\040', '\061', '\012', '\132', '\161', '\162', + '\040', '\161', '\165', '\040', '\061', '\012', '\107', '\153', + '\171', '\040', '\153', '\141', '\040', '\061', '\012', '\130', + '\155', '\162', '\040', '\145', '\162', '\040', '\061', '\012', + '\114', '\156', '\172', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\131', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\171', '\122', '\154', '\040', '\154', '\145', + '\040', '\061', '\012', '\147', '\155', '\113', '\040', '\156', + '\147', '\040', '\061', '\012', '\166', '\167', '\120', '\040', + '\166', '\141', '\040', '\061', '\012', '\145', '\106', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\116', '\152', + '\144', '\040', '\144', '\145', '\040', '\061', '\012', '\153', + '\154', '\107', '\040', '\154', '\145', '\040', '\061', '\012', + '\150', '\142', '\105', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\127', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\161', '\160', '\115', '\040', '\161', '\165', + '\040', '\061', '\012', '\157', '\132', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\152', '\122', '\155', '\040', + '\151', '\152', '\040', '\061', '\012', '\167', '\130', '\154', + '\040', '\154', '\145', '\040', '\061', '\012', '\151', '\171', + '\104', '\040', '\151', '\156', '\040', '\061', '\012', '\146', + '\166', '\114', '\040', '\166', '\141', '\040', '\061', '\012', + '\162', '\120', '\167', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\144', '\122', '\040', '\144', '\145', '\040', + '\061', '\012', '\151', '\123', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\144', '\142', '\121', '\040', '\144', + '\145', '\040', '\061', '\012', '\170', '\170', '\121', '\040', + '\170', '\145', '\040', '\061', '\012', '\104', '\152', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\171', '\147', + '\113', '\040', '\156', '\147', '\040', '\061', '\012', '\122', + '\150', '\142', '\040', '\164', '\150', '\040', '\061', '\012', + '\172', '\147', '\107', '\040', '\156', '\147', '\040', '\061', + '\012', '\131', '\153', '\171', '\040', '\153', '\141', '\040', + '\061', '\012', '\103', '\170', '\152', '\040', '\151', '\152', + '\040', '\061', '\012', '\167', '\127', '\153', '\040', '\153', + '\141', '\040', '\061', '\012', '\154', '\155', '\131', '\040', + '\154', '\145', '\040', '\061', '\012', '\161', '\162', '\102', + '\040', '\161', '\165', '\040', '\061', '\012', '\171', '\167', + '\113', '\040', '\167', '\141', '\040', '\061', '\012', '\170', + '\161', '\111', '\040', '\161', '\165', '\040', '\061', '\012', + '\124', '\167', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\130', '\147', '\161', '\040', '\156', '\147', '\040', + '\061', '\012', '\144', '\167', '\132', '\040', '\144', '\145', + '\040', '\061', '\012', '\156', '\121', '\154', '\040', '\141', + '\156', '\040', '\061', '\012', '\107', '\150', '\143', '\040', + '\164', '\150', '\040', '\061', '\012', '\160', '\156', '\110', + '\040', '\141', '\156', '\040', '\061', '\012', '\166', '\155', + '\125', '\040', '\166', '\141', '\040', '\061', '\012', '\161', + '\161', '\113', '\040', '\161', '\165', '\040', '\061', '\012', + '\143', '\152', '\102', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\172', '\123', '\040', '\156', '\147', '\040', + '\061', '\012', '\122', '\167', '\172', '\040', '\163', '\172', + '\040', '\061', '\012', '\147', '\131', '\162', '\040', '\156', + '\147', '\040', '\061', '\012', '\106', '\147', '\170', '\040', + '\156', '\147', '\040', '\061', '\012', '\167', '\144', '\113', + '\040', '\144', '\145', '\040', '\061', '\012', '\150', '\170', + '\132', '\040', '\164', '\150', '\040', '\061', '\012', '\170', + '\125', '\170', '\040', '\170', '\145', '\040', '\061', '\012', + '\167', '\155', '\124', '\040', '\155', '\145', '\040', '\061', + '\012', '\171', '\131', '\153', '\040', '\153', '\141', '\040', + '\061', '\012', '\146', '\143', '\104', '\040', '\143', '\150', + '\040', '\061', '\012', '\150', '\126', '\166', '\040', '\164', + '\150', '\040', '\061', '\012', '\123', '\147', '\166', '\040', + '\156', '\147', '\040', '\061', '\012', '\172', '\120', '\156', + '\040', '\141', '\156', '\040', '\061', '\012', '\166', '\131', + '\142', '\040', '\166', '\141', '\040', '\061', '\012', '\142', + '\172', '\105', '\040', '\163', '\172', '\040', '\061', '\012', + '\167', '\150', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\116', '\172', '\040', '\161', '\165', '\040', + '\061', '\012', '\167', '\164', '\123', '\040', '\164', '\150', + '\040', '\061', '\012', '\166', '\150', '\131', '\040', '\164', + '\150', '\040', '\061', '\012', '\156', '\114', '\146', '\040', + '\141', '\156', '\040', '\061', '\012', '\114', '\146', '\167', + '\040', '\167', '\141', '\040', '\061', '\012', '\147', '\126', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\147', + '\153', '\123', '\040', '\156', '\147', '\040', '\061', '\012', + '\112', '\161', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\127', '\170', '\040', '\164', '\150', '\040', + '\061', '\012', '\172', '\147', '\117', '\040', '\156', '\147', + '\040', '\061', '\012', '\164', '\147', '\130', '\040', '\164', + '\150', '\040', '\061', '\012', '\152', '\120', '\142', '\040', + '\151', '\152', '\040', '\061', '\012', '\127', '\170', '\142', + '\040', '\142', '\145', '\040', '\061', '\012', '\147', '\161', + '\167', '\040', '\156', '\147', '\040', '\061', '\012', '\103', + '\146', '\167', '\040', '\167', '\141', '\040', '\061', '\012', + '\167', '\157', '\125', '\040', '\157', '\156', '\040', '\061', + '\012', '\171', '\143', '\112', '\040', '\143', '\150', '\040', + '\061', '\012', '\153', '\167', '\104', '\040', '\153', '\141', + '\040', '\061', '\012', '\123', '\142', '\160', '\040', '\160', + '\162', '\040', '\061', '\012', '\161', '\143', '\167', '\040', + '\143', '\150', '\040', '\061', '\012', '\110', '\167', '\162', + '\040', '\145', '\162', '\040', '\061', '\012', '\142', '\155', + '\114', '\040', '\155', '\145', '\040', '\061', '\012', '\147', + '\167', '\132', '\040', '\156', '\147', '\040', '\061', '\012', + '\171', '\113', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\130', '\166', '\040', '\166', '\141', '\040', + '\061', '\012', '\151', '\113', '\170', '\040', '\151', '\156', + '\040', '\061', '\012', '\154', '\122', '\172', '\040', '\154', + '\145', '\040', '\061', '\012', '\143', '\110', '\152', '\040', + '\143', '\150', '\040', '\061', '\012', '\146', '\106', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\163', '\112', + '\166', '\040', '\163', '\172', '\040', '\061', '\012', '\170', + '\155', '\111', '\040', '\155', '\145', '\040', '\061', '\012', + '\143', '\103', '\144', '\040', '\143', '\150', '\040', '\061', + '\012', '\151', '\131', '\144', '\040', '\151', '\156', '\040', + '\061', '\012', '\171', '\146', '\131', '\040', '\156', '\171', + '\040', '\061', '\012', '\170', '\142', '\131', '\040', '\142', + '\145', '\040', '\061', '\012', '\142', '\155', '\105', '\040', + '\155', '\145', '\040', '\061', '\012', '\146', '\102', '\166', + '\040', '\166', '\141', '\040', '\061', '\012', '\144', '\110', + '\167', '\040', '\144', '\145', '\040', '\061', '\012', '\171', + '\143', '\122', '\040', '\143', '\150', '\040', '\061', '\012', + '\167', '\166', '\114', '\040', '\166', '\141', '\040', '\061', + '\012', '\162', '\152', '\114', '\040', '\145', '\162', '\040', + '\061', '\012', '\163', '\131', '\166', '\040', '\163', '\172', + '\040', '\061', '\012', '\127', '\160', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\172', '\170', '\102', '\040', + '\163', '\172', '\040', '\061', '\012', '\171', '\102', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\147', '\144', + '\112', '\040', '\156', '\147', '\040', '\061', '\012', '\131', + '\152', '\157', '\040', '\157', '\156', '\040', '\061', '\012', + '\146', '\160', '\121', '\040', '\160', '\162', '\040', '\061', + '\012', '\161', '\117', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\127', '\152', '\146', '\040', '\151', '\152', + '\040', '\061', '\012', '\161', '\143', '\124', '\040', '\143', + '\150', '\040', '\061', '\012', '\114', '\146', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\143', '\106', '\152', + '\040', '\143', '\150', '\040', '\061', '\012', '\154', '\115', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\167', + '\123', '\146', '\040', '\167', '\141', '\040', '\061', '\012', + '\167', '\121', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\104', '\171', '\040', '\163', '\172', '\040', + '\061', '\012', '\161', '\162', '\154', '\040', '\161', '\165', + '\040', '\061', '\012', '\160', '\131', '\167', '\040', '\160', + '\162', '\040', '\061', '\012', '\126', '\156', '\146', '\040', + '\141', '\156', '\040', '\061', '\012', '\110', '\143', '\152', + '\040', '\143', '\150', '\040', '\061', '\012', '\172', '\144', + '\125', '\040', '\163', '\172', '\040', '\061', '\012', '\142', + '\166', '\120', '\040', '\166', '\141', '\040', '\061', '\012', + '\131', '\146', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\121', '\153', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\167', '\110', '\155', '\040', '\155', '\145', + '\040', '\061', '\012', '\161', '\126', '\166', '\040', '\161', + '\165', '\040', '\061', '\012', '\147', '\153', '\126', '\040', + '\156', '\147', '\040', '\061', '\012', '\166', '\160', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\150', '\106', + '\153', '\040', '\164', '\150', '\040', '\061', '\012', '\146', + '\127', '\146', '\040', '\146', '\157', '\040', '\061', '\012', + '\160', '\131', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\116', '\166', '\040', '\144', '\145', '\040', + '\061', '\012', '\127', '\167', '\152', '\040', '\151', '\152', + '\040', '\061', '\012', '\106', '\155', '\170', '\040', '\155', + '\145', '\040', '\061', '\012', '\155', '\104', '\154', '\040', + '\154', '\145', '\040', '\061', '\012', '\152', '\115', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\146', '\132', + '\153', '\040', '\153', '\141', '\040', '\061', '\012', '\152', + '\116', '\160', '\040', '\151', '\152', '\040', '\061', '\012', + '\161', '\150', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\126', '\142', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\154', '\113', '\170', '\040', '\154', '\145', + '\040', '\061', '\012', '\151', '\132', '\170', '\040', '\151', + '\156', '\040', '\061', '\012', '\163', '\152', '\124', '\040', + '\163', '\172', '\040', '\061', '\012', '\151', '\152', '\131', + '\040', '\151', '\156', '\040', '\061', '\012', '\161', '\164', + '\126', '\040', '\164', '\150', '\040', '\061', '\012', '\171', + '\124', '\153', '\040', '\153', '\141', '\040', '\061', '\012', + '\110', '\160', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\151', '\107', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\171', '\161', '\127', '\040', '\161', '\165', + '\040', '\061', '\012', '\150', '\147', '\106', '\040', '\164', + '\150', '\040', '\061', '\012', '\155', '\106', '\153', '\040', + '\153', '\141', '\040', '\061', '\012', '\117', '\161', '\167', + '\040', '\161', '\165', '\040', '\061', '\012', '\144', '\130', + '\141', '\040', '\141', '\156', '\040', '\061', '\012', '\132', + '\142', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\154', '\113', '\155', '\040', '\154', '\145', '\040', '\061', + '\012', '\123', '\166', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\172', '\113', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\126', '\155', '\172', '\040', '\163', + '\172', '\040', '\061', '\012', '\155', '\111', '\170', '\040', + '\155', '\145', '\040', '\061', '\012', '\147', '\113', '\152', + '\040', '\156', '\147', '\040', '\061', '\012', '\147', '\124', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\166', + '\146', '\103', '\040', '\146', '\157', '\040', '\061', '\012', + '\150', '\113', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\123', '\170', '\040', '\164', '\150', '\040', + '\061', '\012', '\157', '\113', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\156', '\121', '\163', '\040', '\141', + '\156', '\040', '\061', '\012', '\171', '\151', '\107', '\040', + '\151', '\156', '\040', '\061', '\012', '\161', '\147', '\115', + '\040', '\156', '\147', '\040', '\061', '\012', '\153', '\121', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\103', + '\152', '\144', '\040', '\144', '\145', '\040', '\061', '\012', + '\152', '\120', '\171', '\040', '\151', '\152', '\040', '\061', + '\012', '\130', '\161', '\145', '\040', '\161', '\165', '\040', + '\061', '\012', '\120', '\172', '\171', '\040', '\163', '\172', + '\040', '\061', '\012', '\106', '\164', '\161', '\040', '\164', + '\150', '\040', '\061', '\012', '\146', '\143', '\105', '\040', + '\143', '\150', '\040', '\061', '\012', '\155', '\153', '\114', + '\040', '\153', '\141', '\040', '\061', '\012', '\110', '\172', + '\152', '\040', '\163', '\172', '\040', '\061', '\012', '\142', + '\124', '\156', '\040', '\141', '\156', '\040', '\061', '\012', + '\161', '\130', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\155', '\115', '\040', '\144', '\145', '\040', + '\061', '\012', '\144', '\126', '\170', '\040', '\144', '\145', + '\040', '\061', '\012', '\124', '\161', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\170', '\127', '\152', '\040', + '\151', '\152', '\040', '\061', '\012', '\161', '\170', '\121', + '\040', '\161', '\165', '\040', '\061', '\012', '\146', '\121', + '\170', '\040', '\146', '\157', '\040', '\061', '\012', '\166', + '\114', '\154', '\040', '\154', '\145', '\040', '\061', '\012', + '\120', '\147', '\153', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\110', '\153', '\040', '\156', '\147', '\040', + '\061', '\012', '\150', '\170', '\126', '\040', '\164', '\150', + '\040', '\061', '\012', '\164', '\112', '\172', '\040', '\164', + '\150', '\040', '\061', '\012', '\146', '\115', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\111', '\170', '\142', + '\040', '\142', '\145', '\040', '\061', '\012', '\103', '\171', + '\171', '\040', '\156', '\171', '\040', '\061', '\012', '\160', + '\130', '\146', '\040', '\160', '\162', '\040', '\061', '\012', + '\160', '\114', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\124', '\167', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\104', '\164', '\167', '\040', '\164', '\150', + '\040', '\061', '\012', '\167', '\122', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\165', '\130', '\154', '\040', + '\161', '\165', '\040', '\061', '\012', '\172', '\150', '\161', + '\040', '\164', '\150', '\040', '\061', '\012', '\167', '\111', + '\166', '\040', '\166', '\141', '\040', '\061', '\012', '\143', + '\152', '\114', '\040', '\143', '\150', '\040', '\061', '\012', + '\161', '\170', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\104', '\155', '\040', '\154', '\145', '\040', + '\061', '\012', '\164', '\130', '\166', '\040', '\164', '\150', + '\040', '\061', '\012', '\147', '\152', '\103', '\040', '\156', + '\147', '\040', '\061', '\012', '\132', '\172', '\144', '\040', + '\163', '\172', '\040', '\061', '\012', '\164', '\147', '\124', + '\040', '\164', '\150', '\040', '\061', '\012', '\150', '\156', + '\120', '\040', '\164', '\150', '\040', '\061', '\012', '\113', + '\152', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\147', '\126', '\167', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\142', '\111', '\040', '\142', '\145', '\040', + '\061', '\012', '\132', '\160', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\142', '\146', '\117', '\040', '\142', + '\145', '\040', '\061', '\012', '\155', '\123', '\170', '\040', + '\155', '\145', '\040', '\061', '\012', '\161', '\141', '\106', + '\040', '\141', '\156', '\040', '\061', '\012', '\141', '\121', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\110', + '\152', '\144', '\040', '\144', '\145', '\040', '\061', '\012', + '\161', '\130', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\161', '\101', '\040', '\161', '\165', '\040', + '\061', '\012', '\142', '\166', '\122', '\040', '\166', '\141', + '\040', '\061', '\012', '\161', '\123', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\143', '\144', '\126', '\040', + '\143', '\150', '\040', '\061', '\012', '\160', '\124', '\146', + '\040', '\160', '\162', '\040', '\061', '\012', '\113', '\172', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\161', + '\164', '\111', '\040', '\164', '\150', '\040', '\061', '\012', + '\145', '\147', '\131', '\040', '\156', '\147', '\040', '\061', + '\012', '\122', '\170', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\142', '\150', '\131', '\040', '\164', '\150', + '\040', '\061', '\012', '\160', '\107', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\152', '\104', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\146', '\157', '\131', + '\040', '\157', '\156', '\040', '\061', '\012', '\144', '\113', + '\163', '\040', '\163', '\172', '\040', '\061', '\012', '\161', + '\112', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\130', '\167', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\111', '\170', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\162', '\115', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\172', '\130', '\165', '\040', '\161', + '\165', '\040', '\061', '\012', '\163', '\121', '\171', '\040', + '\163', '\172', '\040', '\061', '\012', '\116', '\160', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\121', '\146', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\162', + '\114', '\155', '\040', '\145', '\162', '\040', '\061', '\012', + '\172', '\107', '\155', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\110', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\166', '\143', '\131', '\040', '\143', '\150', + '\040', '\061', '\012', '\153', '\161', '\132', '\040', '\161', + '\165', '\040', '\061', '\012', '\152', '\104', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\161', '\147', '\107', + '\040', '\156', '\147', '\040', '\061', '\012', '\104', '\161', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\146', + '\155', '\117', '\040', '\155', '\145', '\040', '\061', '\012', + '\161', '\144', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\116', '\167', '\040', '\144', '\145', '\040', + '\061', '\012', '\162', '\130', '\152', '\040', '\145', '\162', + '\040', '\061', '\012', '\112', '\167', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\155', '\104', '\142', '\040', + '\155', '\145', '\040', '\061', '\012', '\167', '\115', '\167', + '\040', '\167', '\141', '\040', '\061', '\012', '\131', '\152', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\146', + '\152', '\131', '\040', '\151', '\152', '\040', '\061', '\012', + '\151', '\112', '\142', '\040', '\151', '\156', '\040', '\061', + '\012', '\143', '\144', '\103', '\040', '\143', '\150', '\040', + '\061', '\012', '\131', '\170', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\126', '\142', '\153', '\040', '\153', + '\141', '\040', '\061', '\012', '\106', '\160', '\170', '\040', + '\160', '\162', '\040', '\061', '\012', '\172', '\150', '\104', + '\040', '\164', '\150', '\040', '\061', '\012', '\150', '\103', + '\163', '\040', '\164', '\150', '\040', '\061', '\012', '\144', + '\130', '\167', '\040', '\144', '\145', '\040', '\061', '\012', + '\153', '\104', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\165', '\161', '\124', '\040', '\165', '\156', '\040', + '\061', '\012', '\102', '\170', '\167', '\040', '\167', '\141', + '\040', '\061', '\012', '\102', '\152', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\152', '\107', '\170', '\040', + '\151', '\152', '\040', '\061', '\012', '\146', '\130', '\142', + '\040', '\142', '\145', '\040', '\061', '\012', '\171', '\142', + '\106', '\040', '\142', '\145', '\040', '\061', '\012', '\144', + '\164', '\101', '\040', '\164', '\150', '\040', '\061', '\012', + '\143', '\126', '\166', '\040', '\143', '\150', '\040', '\061', + '\012', '\103', '\142', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\167', '\164', '\110', '\040', '\164', '\150', + '\040', '\061', '\012', '\113', '\144', '\152', '\040', '\144', + '\145', '\040', '\061', '\012', '\153', '\120', '\163', '\040', + '\163', '\172', '\040', '\061', '\012', '\132', '\166', '\153', + '\040', '\153', '\141', '\040', '\061', '\012', '\170', '\120', + '\166', '\040', '\166', '\141', '\040', '\061', '\012', '\167', + '\157', '\110', '\040', '\157', '\156', '\040', '\061', '\012', + '\130', '\160', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\130', '\145', '\040', '\161', '\165', '\040', + '\061', '\012', '\160', '\124', '\152', '\040', '\151', '\152', + '\040', '\061', '\012', '\153', '\167', '\121', '\040', '\153', + '\141', '\040', '\061', '\012', '\153', '\132', '\146', '\040', + '\153', '\141', '\040', '\061', '\012', '\125', '\161', '\152', + '\040', '\161', '\165', '\040', '\061', '\012', '\171', '\112', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\150', + '\103', '\161', '\040', '\164', '\150', '\040', '\061', '\012', + '\152', '\115', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\160', '\150', '\131', '\040', '\164', '\150', '\040', + '\061', '\012', '\153', '\142', '\102', '\040', '\153', '\141', + '\040', '\061', '\012', '\107', '\160', '\172', '\040', '\163', + '\172', '\040', '\061', '\012', '\163', '\107', '\172', '\040', + '\163', '\164', '\040', '\061', '\012', '\146', '\167', '\105', + '\040', '\167', '\141', '\040', '\061', '\012', '\124', '\164', + '\146', '\040', '\164', '\150', '\040', '\061', '\012', '\107', + '\161', '\155', '\040', '\161', '\165', '\040', '\061', '\012', + '\142', '\172', '\116', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\153', '\117', '\040', '\153', '\141', '\040', + '\061', '\012', '\165', '\172', '\127', '\040', '\161', '\165', + '\040', '\061', '\012', '\157', '\170', '\121', '\040', '\157', + '\156', '\040', '\061', '\012', '\126', '\147', '\155', '\040', + '\156', '\147', '\040', '\061', '\012', '\161', '\155', '\104', + '\040', '\161', '\165', '\040', '\061', '\012', '\170', '\161', + '\156', '\040', '\141', '\156', '\040', '\061', '\012', '\166', + '\122', '\154', '\040', '\154', '\145', '\040', '\061', '\012', + '\124', '\156', '\162', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\152', '\127', '\040', '\163', '\172', '\040', + '\061', '\012', '\166', '\167', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\152', '\164', '\127', '\040', '\164', + '\150', '\040', '\061', '\012', '\161', '\156', '\114', '\040', + '\141', '\156', '\040', '\061', '\012', '\171', '\104', '\170', + '\040', '\156', '\171', '\040', '\061', '\012', '\170', '\146', + '\121', '\040', '\146', '\157', '\040', '\061', '\012', '\167', + '\170', '\112', '\040', '\167', '\141', '\040', '\061', '\012', + '\156', '\170', '\105', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\121', '\156', '\040', '\151', '\156', '\040', + '\061', '\012', '\127', '\153', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\171', '\167', '\104', '\040', '\167', + '\141', '\040', '\061', '\012', '\160', '\106', '\146', '\040', + '\160', '\162', '\040', '\061', '\012', '\154', '\142', '\113', + '\040', '\154', '\145', '\040', '\061', '\012', '\166', '\110', + '\171', '\040', '\166', '\141', '\040', '\061', '\012', '\147', + '\126', '\152', '\040', '\156', '\147', '\040', '\061', '\012', + '\117', '\161', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\143', '\116', '\040', '\143', '\150', '\040', + '\061', '\012', '\164', '\127', '\155', '\040', '\164', '\150', + '\040', '\061', '\012', '\167', '\115', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\156', '\167', '\121', '\040', + '\141', '\156', '\040', '\061', '\012', '\161', '\156', '\115', + '\040', '\141', '\156', '\040', '\061', '\012', '\132', '\164', + '\170', '\040', '\164', '\150', '\040', '\061', '\012', '\156', + '\121', '\152', '\040', '\141', '\156', '\040', '\061', '\012', + '\126', '\170', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\125', '\170', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\160', '\127', '\166', '\040', '\166', '\141', + '\040', '\061', '\012', '\171', '\122', '\170', '\040', '\156', + '\171', '\040', '\061', '\012', '\161', '\113', '\165', '\040', + '\165', '\156', '\040', '\061', '\012', '\152', '\130', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\152', '\160', + '\130', '\040', '\151', '\152', '\040', '\061', '\012', '\144', + '\153', '\107', '\040', '\144', '\145', '\040', '\061', '\012', + '\102', '\156', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\131', '\153', '\146', '\040', '\153', '\141', '\040', + '\061', '\012', '\147', '\142', '\127', '\040', '\156', '\147', + '\040', '\061', '\012', '\153', '\154', '\130', '\040', '\154', + '\145', '\040', '\061', '\012', '\166', '\153', '\110', '\040', + '\153', '\141', '\040', '\061', '\012', '\144', '\113', '\144', + '\040', '\144', '\145', '\040', '\061', '\012', '\113', '\160', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\147', + '\161', '\115', '\040', '\156', '\147', '\040', '\061', '\012', + '\171', '\102', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\162', '\120', '\152', '\040', '\145', '\162', '\040', + '\061', '\012', '\110', '\172', '\166', '\040', '\163', '\172', + '\040', '\061', '\012', '\167', '\131', '\172', '\040', '\163', + '\172', '\040', '\061', '\012', '\161', '\107', '\141', '\040', + '\141', '\156', '\040', '\061', '\012', '\152', '\111', '\163', + '\040', '\163', '\172', '\040', '\061', '\012', '\142', '\125', + '\152', '\040', '\151', '\152', '\040', '\061', '\012', '\162', + '\124', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\156', '\161', '\111', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\146', '\120', '\040', '\151', '\152', '\040', + '\061', '\012', '\150', '\122', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\171', '\122', '\162', '\040', '\145', + '\162', '\040', '\061', '\012', '\152', '\152', '\113', '\040', + '\151', '\152', '\040', '\061', '\012', '\164', '\146', '\105', + '\040', '\164', '\150', '\040', '\061', '\012', '\121', '\163', + '\167', '\040', '\163', '\164', '\040', '\061', '\012', '\106', + '\143', '\155', '\040', '\143', '\150', '\040', '\061', '\012', + '\142', '\112', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\164', '\130', '\161', '\040', '\164', '\150', '\040', + '\061', '\012', '\146', '\122', '\154', '\040', '\154', '\145', + '\040', '\061', '\012', '\147', '\161', '\105', '\040', '\156', + '\147', '\040', '\061', '\012', '\167', '\107', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\147', '\113', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\171', '\130', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\172', + '\102', '\171', '\040', '\163', '\172', '\040', '\061', '\012', + '\154', '\124', '\144', '\040', '\154', '\145', '\040', '\061', + '\012', '\127', '\161', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\106', '\164', '\146', '\040', '\164', '\150', + '\040', '\061', '\012', '\167', '\144', '\102', '\040', '\144', + '\145', '\040', '\061', '\012', '\170', '\156', '\130', '\040', + '\141', '\156', '\040', '\061', '\012', '\102', '\161', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\172', '\161', + '\117', '\040', '\161', '\165', '\040', '\061', '\012', '\121', + '\144', '\154', '\040', '\154', '\145', '\040', '\061', '\012', + '\157', '\152', '\112', '\040', '\157', '\156', '\040', '\061', + '\012', '\161', '\132', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\150', '\172', '\127', '\040', '\164', '\150', + '\040', '\061', '\012', '\171', '\154', '\121', '\040', '\154', + '\145', '\040', '\061', '\012', '\132', '\142', '\167', '\040', + '\167', '\141', '\040', '\061', '\012', '\155', '\166', '\114', + '\040', '\166', '\141', '\040', '\061', '\012', '\114', '\152', + '\142', '\040', '\151', '\152', '\040', '\061', '\012', '\107', + '\161', '\145', '\040', '\161', '\165', '\040', '\061', '\012', + '\155', '\146', '\105', '\040', '\155', '\145', '\040', '\061', + '\012', '\170', '\121', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\146', '\114', '\166', '\040', '\166', '\141', + '\040', '\061', '\012', '\170', '\114', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\167', '\102', '\152', '\040', + '\151', '\152', '\040', '\061', '\012', '\152', '\125', '\155', + '\040', '\151', '\152', '\040', '\061', '\012', '\160', '\144', + '\114', '\040', '\144', '\145', '\040', '\061', '\012', '\155', + '\112', '\166', '\040', '\166', '\141', '\040', '\061', '\012', + '\144', '\170', '\125', '\040', '\144', '\145', '\040', '\061', + '\012', '\170', '\161', '\116', '\040', '\161', '\165', '\040', + '\061', '\012', '\146', '\160', '\107', '\040', '\160', '\162', + '\040', '\061', '\012', '\164', '\154', '\117', '\040', '\164', + '\150', '\040', '\061', '\012', '\167', '\150', '\114', '\040', + '\164', '\150', '\040', '\061', '\012', '\153', '\104', '\170', + '\040', '\153', '\141', '\040', '\061', '\012', '\122', '\161', + '\142', '\040', '\161', '\165', '\040', '\061', '\012', '\165', + '\166', '\130', '\040', '\161', '\165', '\040', '\061', '\012', + '\166', '\152', '\131', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\162', '\121', '\040', '\143', '\150', '\040', + '\061', '\012', '\170', '\171', '\131', '\040', '\156', '\171', + '\040', '\061', '\012', '\171', '\150', '\121', '\040', '\164', + '\150', '\040', '\061', '\012', '\171', '\131', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\114', '\155', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\112', '\163', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\107', + '\142', '\152', '\040', '\151', '\152', '\040', '\061', '\012', + '\141', '\120', '\142', '\040', '\141', '\156', '\040', '\061', + '\012', '\144', '\167', '\112', '\040', '\144', '\145', '\040', + '\061', '\012', '\130', '\171', '\166', '\040', '\166', '\141', + '\040', '\061', '\012', '\165', '\143', '\112', '\040', '\143', + '\150', '\040', '\061', '\012', '\144', '\124', '\146', '\040', + '\144', '\145', '\040', '\061', '\012', '\154', '\102', '\142', + '\040', '\154', '\145', '\040', '\061', '\012', '\150', '\113', + '\172', '\040', '\164', '\150', '\040', '\061', '\012', '\152', + '\143', '\122', '\040', '\143', '\150', '\040', '\061', '\012', + '\145', '\121', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\131', '\151', '\040', '\151', '\156', '\040', + '\061', '\012', '\126', '\164', '\142', '\040', '\164', '\150', + '\040', '\061', '\012', '\103', '\143', '\147', '\040', '\143', + '\150', '\040', '\061', '\012', '\172', '\101', '\145', '\040', + '\145', '\162', '\040', '\061', '\012', '\147', '\170', '\112', + '\040', '\156', '\147', '\040', '\061', '\012', '\165', '\166', + '\103', '\040', '\161', '\165', '\040', '\061', '\012', '\102', + '\150', '\155', '\040', '\155', '\141', '\040', '\061', '\012', + '\132', '\147', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\171', '\172', '\112', '\040', '\163', '\172', '\040', + '\061', '\012', '\143', '\166', '\112', '\040', '\143', '\150', + '\040', '\061', '\012', '\170', '\124', '\153', '\040', '\153', + '\141', '\040', '\061', '\012', '\161', '\144', '\113', '\040', + '\161', '\165', '\040', '\061', '\012', '\166', '\167', '\107', + '\040', '\166', '\141', '\040', '\061', '\012', '\131', '\155', + '\170', '\040', '\155', '\145', '\040', '\061', '\012', '\157', + '\131', '\167', '\040', '\157', '\156', '\040', '\061', '\012', + '\152', '\130', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\167', '\146', '\040', '\167', '\141', '\040', + '\061', '\012', '\166', '\126', '\170', '\040', '\166', '\151', + '\040', '\061', '\012', '\122', '\167', '\155', '\040', '\155', + '\145', '\040', '\061', '\012', '\104', '\166', '\153', '\040', + '\153', '\141', '\040', '\061', '\012', '\170', '\113', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\161', '\114', + '\160', '\040', '\161', '\165', '\040', '\061', '\012', '\131', + '\171', '\166', '\040', '\166', '\151', '\040', '\061', '\012', + '\103', '\161', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\122', '\146', '\040', '\146', '\157', '\040', + '\061', '\012', '\121', '\161', '\153', '\040', '\161', '\165', + '\040', '\061', '\012', '\112', '\161', '\145', '\040', '\161', + '\165', '\040', '\061', '\012', '\171', '\132', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\166', '\161', '\107', + '\040', '\161', '\165', '\040', '\061', '\012', '\150', '\142', + '\117', '\040', '\164', '\150', '\040', '\061', '\012', '\165', + '\126', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\122', '\154', '\155', '\040', '\154', '\145', '\040', '\061', + '\012', '\165', '\132', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\120', '\160', '\166', '\040', '\166', '\141', + '\040', '\061', '\012', '\160', '\126', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\171', '\126', '\144', '\040', + '\144', '\145', '\040', '\061', '\012', '\172', '\112', '\154', + '\040', '\154', '\145', '\040', '\061', '\012', '\131', '\172', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\103', + '\166', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\160', '\167', '\123', '\040', '\160', '\162', '\040', '\061', + '\012', '\113', '\153', '\167', '\040', '\153', '\141', '\040', + '\061', '\012', '\127', '\166', '\166', '\040', '\166', '\141', + '\040', '\061', '\012', '\106', '\144', '\171', '\040', '\144', + '\145', '\040', '\061', '\012', '\160', '\160', '\130', '\040', + '\160', '\162', '\040', '\061', '\012', '\150', '\166', '\103', + '\040', '\164', '\150', '\040', '\061', '\012', '\151', '\167', + '\107', '\040', '\151', '\156', '\040', '\061', '\012', '\162', + '\102', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\150', '\102', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\131', '\163', '\040', '\141', '\156', '\040', + '\061', '\012', '\153', '\143', '\117', '\040', '\143', '\150', + '\040', '\061', '\012', '\161', '\105', '\145', '\040', '\161', + '\165', '\040', '\061', '\012', '\131', '\142', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\121', '\163', '\156', + '\040', '\141', '\156', '\040', '\061', '\012', '\163', '\166', + '\103', '\040', '\163', '\164', '\040', '\061', '\012', '\161', + '\153', '\104', '\040', '\161', '\165', '\040', '\061', '\012', + '\121', '\151', '\167', '\040', '\151', '\156', '\040', '\061', + '\012', '\107', '\164', '\152', '\040', '\164', '\150', '\040', + '\061', '\012', '\161', '\101', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\167', '\126', '\171', '\040', '\167', + '\141', '\040', '\061', '\012', '\142', '\170', '\124', '\040', + '\142', '\145', '\040', '\061', '\012', '\121', '\150', '\163', + '\040', '\164', '\150', '\040', '\061', '\012', '\164', '\154', + '\130', '\040', '\164', '\150', '\040', '\061', '\012', '\150', + '\142', '\101', '\040', '\164', '\150', '\040', '\061', '\012', + '\121', '\146', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\170', '\127', '\154', '\040', '\154', '\145', '\040', + '\061', '\012', '\170', '\145', '\126', '\040', '\145', '\162', + '\040', '\061', '\012', '\162', '\161', '\107', '\040', '\161', + '\165', '\040', '\061', '\012', '\166', '\161', '\132', '\040', + '\161', '\165', '\040', '\061', '\012', '\152', '\113', '\166', + '\040', '\151', '\152', '\040', '\061', '\012', '\151', '\124', + '\146', '\040', '\151', '\156', '\040', '\061', '\012', '\153', + '\167', '\125', '\040', '\153', '\141', '\040', '\061', '\012', + '\151', '\106', '\161', '\040', '\151', '\156', '\040', '\061', + '\012', '\155', '\152', '\132', '\040', '\151', '\152', '\040', + '\061', '\012', '\170', '\147', '\112', '\040', '\156', '\147', + '\040', '\061', '\012', '\172', '\114', '\160', '\040', '\163', + '\172', '\040', '\061', '\012', '\161', '\163', '\122', '\040', + '\161', '\165', '\040', '\061', '\012', '\172', '\104', '\152', + '\040', '\163', '\172', '\040', '\061', '\012', '\160', '\144', + '\106', '\040', '\144', '\145', '\040', '\061', '\012', '\167', + '\170', '\116', '\040', '\167', '\141', '\040', '\061', '\012', + '\167', '\107', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\144', '\125', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\144', '\112', '\167', '\040', '\144', '\145', + '\040', '\061', '\012', '\146', '\103', '\142', '\040', '\142', + '\145', '\040', '\061', '\012', '\104', '\150', '\172', '\040', + '\164', '\150', '\040', '\061', '\012', '\171', '\111', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\141', '\121', + '\155', '\040', '\141', '\156', '\040', '\061', '\012', '\131', + '\172', '\163', '\040', '\163', '\164', '\040', '\061', '\012', + '\166', '\110', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\142', '\152', '\126', '\040', '\151', '\152', '\040', + '\061', '\012', '\172', '\123', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\127', '\161', '\163', '\040', '\161', + '\165', '\040', '\061', '\012', '\152', '\162', '\127', '\040', + '\145', '\162', '\040', '\061', '\012', '\110', '\172', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\167', '\127', + '\163', '\040', '\163', '\164', '\040', '\061', '\012', '\115', + '\153', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\172', '\147', '\106', '\040', '\156', '\147', '\040', '\061', + '\012', '\103', '\156', '\153', '\040', '\141', '\156', '\040', + '\061', '\012', '\162', '\104', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\146', '\172', '\102', '\040', '\163', + '\172', '\040', '\061', '\012', '\146', '\117', '\155', '\040', + '\155', '\145', '\040', '\061', '\012', '\165', '\126', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\121', '\146', + '\151', '\040', '\151', '\156', '\040', '\061', '\012', '\115', + '\150', '\152', '\040', '\164', '\150', '\040', '\061', '\012', + '\165', '\131', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\122', '\161', '\170', '\040', '\161', '\165', '\040', + '\061', '\012', '\150', '\153', '\131', '\040', '\164', '\150', + '\040', '\061', '\012', '\167', '\131', '\142', '\040', '\167', + '\141', '\040', '\061', '\012', '\164', '\161', '\120', '\040', + '\164', '\150', '\040', '\061', '\012', '\112', '\160', '\142', + '\040', '\160', '\162', '\040', '\061', '\012', '\142', '\107', + '\167', '\040', '\167', '\141', '\040', '\061', '\012', '\170', + '\106', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\130', '\167', '\142', '\040', '\167', '\141', '\040', '\061', + '\012', '\113', '\147', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\111', '\161', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\160', '\112', '\155', '\040', '\155', + '\145', '\040', '\061', '\012', '\121', '\153', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\142', '\126', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\171', '\124', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\172', + '\132', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\143', '\104', '\172', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\146', '\155', '\040', '\161', '\165', '\040', + '\061', '\012', '\141', '\146', '\121', '\040', '\141', '\156', + '\040', '\061', '\012', '\121', '\167', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\142', '\144', '\112', '\040', + '\144', '\145', '\040', '\061', '\012', '\161', '\124', '\165', + '\040', '\165', '\156', '\040', '\061', '\012', '\125', '\143', + '\170', '\040', '\143', '\150', '\040', '\061', '\012', '\110', + '\156', '\170', '\040', '\141', '\156', '\040', '\061', '\012', + '\110', '\142', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\171', '\110', '\040', '\156', '\147', '\040', + '\061', '\012', '\164', '\124', '\172', '\040', '\164', '\150', + '\040', '\061', '\012', '\164', '\170', '\126', '\040', '\164', + '\150', '\040', '\061', '\012', '\142', '\144', '\123', '\040', + '\144', '\145', '\040', '\061', '\012', '\127', '\147', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\157', '\161', + '\120', '\040', '\161', '\165', '\040', '\061', '\012', '\122', + '\162', '\146', '\040', '\145', '\162', '\040', '\061', '\012', + '\147', '\131', '\171', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\115', '\163', '\040', '\163', '\164', '\040', + '\061', '\012', '\146', '\113', '\144', '\040', '\144', '\145', + '\040', '\061', '\012', '\110', '\171', '\170', '\040', '\156', + '\171', '\040', '\061', '\012', '\115', '\170', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\161', '\110', '\153', + '\040', '\161', '\165', '\040', '\061', '\012', '\164', '\146', + '\115', '\040', '\164', '\150', '\040', '\061', '\012', '\150', + '\147', '\121', '\040', '\164', '\150', '\040', '\061', '\012', + '\172', '\155', '\117', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\172', '\123', '\040', '\163', '\172', '\040', + '\061', '\012', '\152', '\167', '\121', '\040', '\151', '\152', + '\040', '\061', '\012', '\106', '\150', '\143', '\040', '\151', + '\143', '\040', '\061', '\012', '\170', '\111', '\171', '\040', + '\156', '\171', '\040', '\061', '\012', '\146', '\110', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\167', '\161', + '\131', '\040', '\161', '\165', '\040', '\061', '\012', '\142', + '\106', '\160', '\040', '\160', '\162', '\040', '\061', '\012', + '\121', '\144', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\150', '\126', '\040', '\164', '\150', '\040', + '\061', '\012', '\142', '\103', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\110', '\147', '\162', '\040', '\156', + '\147', '\040', '\061', '\012', '\170', '\161', '\114', '\040', + '\161', '\165', '\040', '\061', '\012', '\161', '\147', '\123', + '\040', '\156', '\147', '\040', '\061', '\012', '\116', '\161', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\146', + '\121', '\166', '\040', '\166', '\141', '\040', '\061', '\012', + '\121', '\172', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\111', '\170', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\103', '\170', '\155', '\040', '\155', '\145', + '\040', '\061', '\012', '\155', '\170', '\116', '\040', '\155', + '\145', '\040', '\061', '\012', '\166', '\121', '\151', '\040', + '\151', '\156', '\040', '\061', '\012', '\143', '\101', '\161', + '\040', '\143', '\150', '\040', '\061', '\012', '\145', '\103', + '\170', '\040', '\145', '\162', '\040', '\061', '\012', '\155', + '\161', '\130', '\040', '\161', '\165', '\040', '\061', '\012', + '\162', '\161', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\126', '\160', '\040', '\160', '\162', '\040', + '\061', '\012', '\161', '\157', '\120', '\040', '\161', '\165', + '\040', '\061', '\012', '\107', '\170', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\166', '\172', '\130', '\040', + '\163', '\172', '\040', '\061', '\012', '\146', '\130', '\146', + '\040', '\146', '\157', '\040', '\061', '\012', '\121', '\164', + '\143', '\040', '\164', '\150', '\040', '\061', '\012', '\157', + '\150', '\121', '\040', '\164', '\150', '\040', '\061', '\012', + '\131', '\147', '\171', '\040', '\156', '\147', '\040', '\061', + '\012', '\130', '\156', '\142', '\040', '\141', '\156', '\040', + '\061', '\012', '\143', '\127', '\155', '\040', '\143', '\150', + '\040', '\061', '\012', '\152', '\130', '\167', '\040', '\151', + '\152', '\040', '\061', '\012', '\147', '\127', '\152', '\040', + '\156', '\147', '\040', '\061', '\012', '\113', '\155', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\166', '\166', + '\110', '\040', '\166', '\141', '\040', '\061', '\012', '\125', + '\145', '\167', '\040', '\145', '\162', '\040', '\061', '\012', + '\161', '\112', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\110', '\153', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\170', '\155', '\120', '\040', '\155', '\145', + '\040', '\061', '\012', '\163', '\154', '\122', '\040', '\151', + '\163', '\040', '\061', '\012', '\125', '\141', '\161', '\040', + '\141', '\156', '\040', '\061', '\012', '\172', '\142', '\107', + '\040', '\163', '\172', '\040', '\061', '\012', '\166', '\116', + '\166', '\040', '\166', '\141', '\040', '\061', '\012', '\143', + '\126', '\142', '\040', '\143', '\150', '\040', '\061', '\012', + '\142', '\107', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\151', '\167', '\125', '\040', '\151', '\156', '\040', + '\061', '\012', '\103', '\156', '\167', '\040', '\141', '\156', + '\040', '\061', '\012', '\162', '\130', '\144', '\040', '\145', + '\162', '\040', '\061', '\012', '\166', '\127', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\164', '\107', '\146', + '\040', '\164', '\150', '\040', '\061', '\012', '\146', '\142', + '\131', '\040', '\142', '\145', '\040', '\061', '\012', '\150', + '\172', '\160', '\040', '\164', '\150', '\040', '\061', '\012', + '\165', '\127', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\115', '\142', '\040', '\142', '\145', '\040', + '\061', '\012', '\152', '\172', '\127', '\040', '\163', '\172', + '\040', '\061', '\012', '\147', '\114', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\153', '\132', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\153', '\110', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\126', '\167', + '\146', '\040', '\167', '\141', '\040', '\061', '\012', '\166', + '\164', '\131', '\040', '\164', '\150', '\040', '\061', '\012', + '\161', '\145', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\170', '\107', '\040', '\143', '\150', '\040', + '\061', '\012', '\165', '\121', '\172', '\040', '\161', '\165', + '\040', '\061', '\012', '\152', '\107', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\143', '\166', '\101', '\040', + '\143', '\150', '\040', '\061', '\012', '\157', '\124', '\155', + '\040', '\157', '\156', '\040', '\061', '\012', '\160', '\152', + '\131', '\040', '\151', '\152', '\040', '\061', '\012', '\142', + '\125', '\157', '\040', '\157', '\156', '\040', '\061', '\012', + '\152', '\167', '\125', '\040', '\151', '\152', '\040', '\061', + '\012', '\112', '\147', '\155', '\040', '\156', '\147', '\040', + '\061', '\012', '\164', '\146', '\132', '\040', '\164', '\150', + '\040', '\061', '\012', '\170', '\145', '\117', '\040', '\145', + '\162', '\040', '\061', '\012', '\161', '\102', '\160', '\040', + '\161', '\165', '\040', '\061', '\012', '\160', '\102', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\161', '\123', + '\142', '\040', '\161', '\165', '\040', '\061', '\012', '\152', + '\171', '\120', '\040', '\151', '\152', '\040', '\061', '\012', + '\106', '\153', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\152', '\123', '\040', '\141', '\156', '\040', + '\061', '\012', '\152', '\164', '\101', '\040', '\164', '\150', + '\040', '\061', '\012', '\132', '\155', '\146', '\040', '\155', + '\145', '\040', '\061', '\012', '\131', '\164', '\155', '\040', + '\164', '\150', '\040', '\061', '\012', '\120', '\161', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\142', '\167', + '\112', '\040', '\167', '\141', '\040', '\061', '\012', '\157', + '\127', '\146', '\040', '\157', '\156', '\040', '\061', '\012', + '\153', '\170', '\112', '\040', '\153', '\141', '\040', '\061', + '\012', '\152', '\110', '\170', '\040', '\151', '\152', '\040', + '\061', '\012', '\147', '\143', '\120', '\040', '\143', '\150', + '\040', '\061', '\012', '\147', '\102', '\163', '\040', '\156', + '\147', '\040', '\061', '\012', '\142', '\153', '\113', '\040', + '\153', '\141', '\040', '\061', '\012', '\166', '\144', '\121', + '\040', '\144', '\145', '\040', '\061', '\012', '\160', '\152', + '\132', '\040', '\151', '\152', '\040', '\061', '\012', '\126', + '\147', '\146', '\040', '\156', '\147', '\040', '\061', '\012', + '\163', '\166', '\107', '\040', '\163', '\164', '\040', '\061', + '\012', '\153', '\107', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\127', '\152', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\121', '\155', '\153', '\040', '\153', + '\141', '\040', '\061', '\012', '\107', '\154', '\166', '\040', + '\154', '\145', '\040', '\061', '\012', '\164', '\155', '\131', + '\040', '\164', '\150', '\040', '\061', '\012', '\153', '\154', + '\131', '\040', '\154', '\145', '\040', '\061', '\012', '\120', + '\143', '\152', '\040', '\143', '\150', '\040', '\061', '\012', + '\146', '\121', '\167', '\040', '\167', '\151', '\040', '\061', + '\012', '\170', '\141', '\117', '\040', '\141', '\156', '\040', + '\061', '\012', '\152', '\146', '\116', '\040', '\151', '\152', + '\040', '\061', '\012', '\161', '\107', '\170', '\040', '\161', + '\165', '\040', '\061', '\012', '\161', '\166', '\102', '\040', + '\161', '\165', '\040', '\061', '\012', '\150', '\167', '\101', + '\040', '\164', '\150', '\040', '\061', '\012', '\130', '\155', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\130', + '\166', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\102', '\160', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\112', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\166', '\155', '\132', '\040', '\166', '\141', + '\040', '\061', '\012', '\156', '\112', '\160', '\040', '\141', + '\156', '\040', '\061', '\012', '\172', '\161', '\112', '\040', + '\161', '\165', '\040', '\061', '\012', '\161', '\110', '\146', + '\040', '\161', '\165', '\040', '\061', '\012', '\155', '\121', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\171', + '\107', '\172', '\040', '\163', '\172', '\040', '\061', '\012', + '\150', '\121', '\155', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\102', '\160', '\040', '\155', '\145', '\040', + '\061', '\012', '\164', '\160', '\112', '\040', '\164', '\150', + '\040', '\061', '\012', '\121', '\153', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\165', '\125', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\164', '\144', '\112', + '\040', '\164', '\150', '\040', '\061', '\012', '\112', '\146', + '\156', '\040', '\141', '\156', '\040', '\061', '\012', '\114', + '\166', '\152', '\040', '\151', '\152', '\040', '\061', '\012', + '\151', '\130', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\117', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\142', '\150', '\113', '\040', '\164', '\150', + '\040', '\061', '\012', '\142', '\115', '\153', '\040', '\153', + '\141', '\040', '\061', '\012', '\106', '\163', '\167', '\040', + '\163', '\164', '\040', '\061', '\012', '\161', '\101', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\170', '\167', + '\112', '\040', '\167', '\141', '\040', '\061', '\012', '\146', + '\120', '\155', '\040', '\155', '\145', '\040', '\061', '\012', + '\104', '\146', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\132', '\142', '\160', '\040', '\160', '\162', '\040', + '\061', '\012', '\102', '\147', '\167', '\040', '\156', '\147', + '\040', '\061', '\012', '\160', '\121', '\160', '\040', '\160', + '\162', '\040', '\061', '\012', '\153', '\121', '\160', '\040', + '\153', '\141', '\040', '\061', '\012', '\161', '\157', '\126', + '\040', '\161', '\165', '\040', '\061', '\012', '\125', '\161', + '\144', '\040', '\161', '\165', '\040', '\061', '\012', '\152', + '\131', '\157', '\040', '\157', '\156', '\040', '\061', '\012', + '\163', '\104', '\146', '\040', '\163', '\164', '\040', '\061', + '\012', '\170', '\165', '\112', '\040', '\161', '\165', '\040', + '\061', '\012', '\166', '\122', '\153', '\040', '\153', '\141', + '\040', '\061', '\012', '\121', '\163', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\171', '\124', '\144', '\040', + '\144', '\145', '\040', '\061', '\012', '\121', '\170', '\162', + '\040', '\145', '\162', '\040', '\061', '\012', '\110', '\166', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\150', + '\132', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\161', '\104', '\165', '\040', '\165', '\156', '\040', '\061', + '\012', '\146', '\170', '\101', '\040', '\146', '\157', '\040', + '\061', '\012', '\170', '\120', '\146', '\040', '\146', '\157', + '\040', '\061', '\012', '\167', '\130', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\152', '\112', '\142', '\040', + '\151', '\152', '\040', '\061', '\012', '\160', '\144', '\113', + '\040', '\144', '\145', '\040', '\061', '\012', '\147', '\160', + '\127', '\040', '\156', '\147', '\040', '\061', '\012', '\121', + '\147', '\170', '\040', '\156', '\147', '\040', '\061', '\012', + '\153', '\170', '\107', '\040', '\153', '\141', '\040', '\061', + '\012', '\144', '\114', '\170', '\040', '\144', '\145', '\040', + '\061', '\012', '\102', '\167', '\172', '\040', '\163', '\172', + '\040', '\061', '\012', '\126', '\144', '\170', '\040', '\144', + '\145', '\040', '\061', '\012', '\171', '\121', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\127', '\163', '\170', + '\040', '\163', '\164', '\040', '\061', '\012', '\146', '\123', + '\142', '\040', '\142', '\145', '\040', '\061', '\012', '\125', + '\153', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\120', '\152', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\162', '\106', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\146', '\152', '\120', '\040', '\151', '\152', + '\040', '\061', '\012', '\153', '\127', '\166', '\040', '\153', + '\141', '\040', '\061', '\012', '\113', '\150', '\146', '\040', + '\164', '\150', '\040', '\061', '\012', '\171', '\107', '\166', + '\040', '\166', '\141', '\040', '\061', '\012', '\160', '\156', + '\104', '\040', '\141', '\156', '\040', '\061', '\012', '\152', + '\131', '\146', '\040', '\151', '\152', '\040', '\061', '\012', + '\155', '\147', '\122', '\040', '\156', '\147', '\040', '\061', + '\012', '\162', '\152', '\103', '\040', '\145', '\162', '\040', + '\061', '\012', '\130', '\152', '\154', '\040', '\154', '\145', + '\040', '\061', '\012', '\153', '\172', '\105', '\040', '\163', + '\172', '\040', '\061', '\012', '\121', '\147', '\161', '\040', + '\156', '\147', '\040', '\061', '\012', '\172', '\147', '\142', + '\040', '\156', '\147', '\040', '\061', '\012', '\155', '\150', + '\104', '\040', '\164', '\150', '\040', '\061', '\012', '\166', + '\153', '\117', '\040', '\153', '\141', '\040', '\061', '\012', + '\165', '\167', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\120', '\160', '\040', '\145', '\162', '\040', + '\061', '\012', '\167', '\130', '\144', '\040', '\144', '\145', + '\040', '\061', '\012', '\147', '\101', '\157', '\040', '\156', + '\147', '\040', '\061', '\012', '\153', '\166', '\107', '\040', + '\153', '\141', '\040', '\061', '\012', '\166', '\143', '\130', + '\040', '\143', '\150', '\040', '\061', '\012', '\170', '\117', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\130', + '\172', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\106', '\155', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\107', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\152', '\152', '\122', '\040', '\151', '\152', + '\040', '\061', '\012', '\161', '\153', '\111', '\040', '\153', + '\165', '\040', '\061', '\012', '\160', '\161', '\110', '\040', + '\161', '\165', '\040', '\061', '\012', '\143', '\156', '\110', + '\040', '\141', '\156', '\040', '\061', '\012', '\144', '\150', + '\124', '\040', '\164', '\150', '\040', '\061', '\012', '\155', + '\144', '\122', '\040', '\144', '\145', '\040', '\061', '\012', + '\144', '\104', '\146', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\111', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\170', '\103', '\152', '\040', '\151', '\152', + '\040', '\061', '\012', '\161', '\122', '\153', '\040', '\161', + '\165', '\040', '\061', '\012', '\153', '\113', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\111', '\165', '\165', + '\040', '\161', '\165', '\040', '\061', '\012', '\152', '\161', + '\122', '\040', '\161', '\165', '\040', '\061', '\012', '\161', + '\105', '\153', '\040', '\161', '\165', '\040', '\061', '\012', + '\150', '\146', '\117', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\165', '\112', '\040', '\165', '\156', '\040', + '\061', '\012', '\156', '\122', '\160', '\040', '\141', '\156', + '\040', '\061', '\012', '\164', '\170', '\111', '\040', '\164', + '\150', '\040', '\061', '\012', '\171', '\146', '\132', '\040', + '\156', '\171', '\040', '\061', '\012', '\157', '\161', '\124', + '\040', '\150', '\157', '\040', '\061', '\012', '\143', '\147', + '\130', '\040', '\143', '\150', '\040', '\061', '\012', '\160', + '\142', '\114', '\040', '\160', '\162', '\040', '\061', '\012', + '\130', '\155', '\170', '\040', '\155', '\145', '\040', '\061', + '\012', '\126', '\152', '\162', '\040', '\145', '\162', '\040', + '\061', '\012', '\171', '\154', '\131', '\040', '\154', '\145', + '\040', '\061', '\012', '\144', '\146', '\113', '\040', '\144', + '\145', '\040', '\061', '\012', '\170', '\147', '\104', '\040', + '\156', '\147', '\040', '\061', '\012', '\165', '\167', '\114', + '\040', '\161', '\165', '\040', '\061', '\012', '\142', '\120', + '\155', '\040', '\155', '\145', '\040', '\061', '\012', '\161', + '\103', '\171', '\040', '\161', '\165', '\040', '\061', '\012', + '\122', '\160', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\161', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\170', '\112', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\154', '\172', '\121', '\040', '\154', + '\145', '\040', '\061', '\012', '\146', '\147', '\115', '\040', + '\156', '\147', '\040', '\061', '\012', '\131', '\154', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\146', '\124', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\122', + '\152', '\146', '\040', '\151', '\152', '\040', '\061', '\012', + '\122', '\147', '\152', '\040', '\152', '\157', '\040', '\061', + '\012', '\107', '\153', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\146', '\170', '\107', '\040', '\146', '\157', + '\040', '\061', '\012', '\155', '\164', '\107', '\040', '\164', + '\150', '\040', '\061', '\012', '\154', '\147', '\112', '\040', + '\156', '\147', '\040', '\061', '\012', '\164', '\144', '\122', + '\040', '\164', '\150', '\040', '\061', '\012', '\151', '\110', + '\153', '\040', '\151', '\156', '\040', '\061', '\012', '\107', + '\161', '\166', '\040', '\161', '\165', '\040', '\061', '\012', + '\154', '\104', '\152', '\040', '\154', '\145', '\040', '\061', + '\012', '\167', '\172', '\132', '\040', '\163', '\172', '\040', + '\061', '\012', '\144', '\106', '\160', '\040', '\144', '\145', + '\040', '\061', '\012', '\161', '\124', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\127', '\164', '\147', '\040', + '\164', '\150', '\040', '\061', '\012', '\143', '\142', '\124', + '\040', '\143', '\150', '\040', '\061', '\012', '\144', '\166', + '\113', '\040', '\144', '\145', '\040', '\061', '\012', '\103', + '\164', '\167', '\040', '\164', '\150', '\040', '\061', '\012', + '\155', '\144', '\107', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\113', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\103', '\154', '\146', '\040', '\154', '\145', + '\040', '\061', '\012', '\167', '\162', '\125', '\040', '\145', + '\162', '\040', '\061', '\012', '\147', '\155', '\124', '\040', + '\156', '\147', '\040', '\061', '\012', '\142', '\130', '\170', + '\040', '\142', '\145', '\040', '\061', '\012', '\172', '\117', + '\170', '\040', '\163', '\172', '\040', '\061', '\012', '\130', + '\156', '\146', '\040', '\141', '\156', '\040', '\061', '\012', + '\162', '\172', '\121', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\121', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\153', '\160', '\124', '\040', '\153', '\141', + '\040', '\061', '\012', '\146', '\131', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\172', '\114', '\162', '\040', + '\145', '\162', '\040', '\061', '\012', '\130', '\147', '\144', + '\040', '\156', '\147', '\040', '\061', '\012', '\143', '\132', + '\154', '\040', '\143', '\150', '\040', '\061', '\012', '\154', + '\106', '\171', '\040', '\154', '\145', '\040', '\061', '\012', + '\132', '\156', '\147', '\040', '\141', '\156', '\040', '\061', + '\012', '\141', '\130', '\147', '\040', '\141', '\156', '\040', + '\061', '\012', '\161', '\142', '\105', '\040', '\161', '\165', + '\040', '\061', '\012', '\172', '\143', '\131', '\040', '\143', + '\150', '\040', '\061', '\012', '\163', '\161', '\113', '\040', + '\161', '\165', '\040', '\061', '\012', '\102', '\154', '\170', + '\040', '\154', '\145', '\040', '\061', '\012', '\157', '\161', + '\112', '\040', '\161', '\165', '\040', '\061', '\012', '\152', + '\120', '\166', '\040', '\151', '\152', '\040', '\061', '\012', + '\161', '\132', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\144', '\132', '\040', '\144', '\145', '\040', + '\061', '\012', '\102', '\161', '\155', '\040', '\161', '\165', + '\040', '\061', '\012', '\143', '\160', '\107', '\040', '\143', + '\150', '\040', '\061', '\012', '\170', '\144', '\120', '\040', + '\144', '\145', '\040', '\061', '\012', '\146', '\165', '\106', + '\040', '\161', '\165', '\040', '\061', '\012', '\166', '\142', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\144', + '\150', '\110', '\040', '\164', '\150', '\040', '\061', '\012', + '\112', '\167', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\161', '\153', '\117', '\040', '\153', '\157', '\040', + '\061', '\012', '\147', '\163', '\131', '\040', '\156', '\147', + '\040', '\061', '\012', '\161', '\107', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\112', '\153', '\166', '\040', + '\153', '\141', '\040', '\061', '\012', '\172', '\160', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\162', '\167', + '\113', '\040', '\145', '\162', '\040', '\061', '\012', '\114', + '\150', '\161', '\040', '\164', '\150', '\040', '\061', '\012', + '\172', '\165', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\161', '\126', '\040', '\161', '\165', '\040', + '\061', '\012', '\121', '\143', '\166', '\040', '\143', '\150', + '\040', '\061', '\012', '\155', '\127', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\143', '\156', '\106', '\040', + '\141', '\156', '\040', '\061', '\012', '\154', '\127', '\167', + '\040', '\154', '\145', '\040', '\061', '\012', '\164', '\170', + '\123', '\040', '\164', '\150', '\040', '\061', '\012', '\172', + '\156', '\105', '\040', '\141', '\156', '\040', '\061', '\012', + '\146', '\124', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\154', '\106', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\167', '\144', '\112', '\040', '\144', '\145', + '\040', '\061', '\012', '\145', '\126', '\153', '\040', '\145', + '\162', '\040', '\061', '\012', '\172', '\152', '\132', '\040', + '\163', '\172', '\040', '\061', '\012', '\146', '\120', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\143', '\161', + '\121', '\040', '\143', '\150', '\040', '\061', '\012', '\120', + '\143', '\147', '\040', '\143', '\150', '\040', '\061', '\012', + '\131', '\144', '\153', '\040', '\144', '\145', '\040', '\061', + '\012', '\163', '\166', '\105', '\040', '\163', '\164', '\040', + '\061', '\012', '\127', '\161', '\142', '\040', '\161', '\165', + '\040', '\061', '\012', '\142', '\143', '\126', '\040', '\143', + '\150', '\040', '\061', '\012', '\156', '\110', '\170', '\040', + '\157', '\156', '\040', '\061', '\012', '\167', '\101', '\170', + '\040', '\167', '\141', '\040', '\061', '\012', '\150', '\146', + '\102', '\040', '\164', '\150', '\040', '\061', '\012', '\141', + '\115', '\166', '\040', '\141', '\156', '\040', '\061', '\012', + '\160', '\167', '\117', '\040', '\160', '\162', '\040', '\061', + '\012', '\131', '\167', '\170', '\040', '\167', '\141', '\040', + '\061', '\012', '\143', '\142', '\110', '\040', '\143', '\150', + '\040', '\061', '\012', '\157', '\152', '\132', '\040', '\157', + '\156', '\040', '\061', '\012', '\163', '\165', '\125', '\040', + '\161', '\165', '\040', '\061', '\012', '\152', '\143', '\125', + '\040', '\143', '\150', '\040', '\061', '\012', '\163', '\161', + '\131', '\040', '\161', '\165', '\040', '\061', '\012', '\152', + '\115', '\162', '\040', '\145', '\162', '\040', '\061', '\012', + '\160', '\170', '\107', '\040', '\160', '\162', '\040', '\061', + '\012', '\162', '\102', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\166', '\154', '\131', '\040', '\154', '\145', + '\040', '\061', '\012', '\150', '\171', '\131', '\040', '\164', + '\150', '\040', '\061', '\012', '\103', '\166', '\167', '\040', + '\166', '\141', '\040', '\061', '\012', '\124', '\161', '\145', + '\040', '\161', '\165', '\040', '\061', '\012', '\146', '\123', + '\152', '\040', '\151', '\152', '\040', '\061', '\012', '\146', + '\126', '\163', '\040', '\163', '\164', '\040', '\061', '\012', + '\105', '\161', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\156', '\104', '\040', '\141', '\156', '\040', + '\061', '\012', '\117', '\167', '\160', '\040', '\160', '\162', + '\040', '\061', '\012', '\170', '\124', '\142', '\040', '\142', + '\145', '\040', '\061', '\012', '\167', '\152', '\114', '\040', + '\151', '\152', '\040', '\061', '\012', '\122', '\170', '\166', + '\040', '\166', '\141', '\040', '\061', '\012', '\156', '\127', + '\146', '\040', '\141', '\156', '\040', '\061', '\012', '\166', + '\110', '\160', '\040', '\166', '\141', '\040', '\061', '\012', + '\166', '\102', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\116', '\161', '\166', '\040', '\161', '\165', '\040', + '\061', '\012', '\114', '\172', '\146', '\040', '\163', '\172', + '\040', '\061', '\012', '\142', '\167', '\123', '\040', '\167', + '\141', '\040', '\061', '\012', '\103', '\142', '\171', '\040', + '\142', '\145', '\040', '\061', '\012', '\172', '\122', '\162', + '\040', '\145', '\162', '\040', '\061', '\012', '\161', '\167', + '\112', '\040', '\161', '\165', '\040', '\061', '\012', '\170', + '\156', '\102', '\040', '\141', '\156', '\040', '\061', '\012', + '\161', '\111', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\107', '\153', '\040', '\143', '\150', '\040', + '\061', '\012', '\131', '\152', '\151', '\040', '\151', '\156', + '\040', '\061', '\012', '\147', '\126', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\154', '\104', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\121', '\171', '\162', + '\040', '\145', '\162', '\040', '\061', '\012', '\146', '\143', + '\110', '\040', '\143', '\150', '\040', '\061', '\012', '\156', + '\170', '\102', '\040', '\141', '\156', '\040', '\061', '\012', + '\144', '\166', '\167', '\040', '\144', '\145', '\040', '\061', + '\012', '\147', '\121', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\155', '\162', '\122', '\040', '\145', '\162', + '\040', '\061', '\012', '\146', '\156', '\113', '\040', '\141', + '\156', '\040', '\061', '\012', '\110', '\154', '\162', '\040', + '\154', '\145', '\040', '\061', '\012', '\104', '\156', '\161', + '\040', '\141', '\156', '\040', '\061', '\012', '\142', '\156', + '\125', '\040', '\141', '\156', '\040', '\061', '\012', '\161', + '\103', '\145', '\040', '\161', '\165', '\040', '\061', '\012', + '\124', '\152', '\166', '\040', '\151', '\152', '\040', '\061', + '\012', '\105', '\160', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\167', '\114', '\146', '\040', '\167', '\141', + '\040', '\061', '\012', '\160', '\132', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\147', '\166', '\122', '\040', + '\156', '\147', '\040', '\061', '\012', '\153', '\161', '\113', + '\040', '\161', '\165', '\040', '\061', '\012', '\166', '\154', + '\107', '\040', '\154', '\145', '\040', '\061', '\012', '\166', + '\166', '\116', '\040', '\166', '\141', '\040', '\061', '\012', + '\147', '\142', '\115', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\116', '\153', '\040', '\153', '\141', '\040', + '\061', '\012', '\152', '\172', '\114', '\040', '\163', '\172', + '\040', '\061', '\012', '\127', '\154', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\141', '\131', '\161', '\040', + '\141', '\156', '\040', '\061', '\012', '\172', '\144', '\131', + '\040', '\144', '\145', '\040', '\061', '\012', '\163', '\146', + '\107', '\040', '\163', '\164', '\040', '\061', '\012', '\161', + '\146', '\127', '\040', '\161', '\165', '\040', '\061', '\012', + '\153', '\102', '\166', '\040', '\153', '\141', '\040', '\061', + '\012', '\142', '\164', '\107', '\040', '\164', '\150', '\040', + '\061', '\012', '\115', '\161', '\142', '\040', '\161', '\165', + '\040', '\061', '\012', '\154', '\162', '\103', '\040', '\145', + '\162', '\040', '\061', '\012', '\166', '\165', '\105', '\040', + '\161', '\165', '\040', '\061', '\012', '\146', '\171', '\112', + '\040', '\156', '\171', '\040', '\061', '\012', '\161', '\155', + '\132', '\040', '\161', '\165', '\040', '\061', '\012', '\112', + '\153', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\103', '\155', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\130', '\171', '\040', '\142', '\145', '\040', + '\061', '\012', '\131', '\155', '\171', '\040', '\155', '\145', + '\040', '\061', '\012', '\161', '\170', '\131', '\040', '\161', + '\165', '\040', '\061', '\012', '\143', '\116', '\154', '\040', + '\143', '\150', '\040', '\061', '\012', '\146', '\172', '\125', + '\040', '\146', '\157', '\040', '\061', '\012', '\122', '\166', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\171', + '\154', '\111', '\040', '\154', '\145', '\040', '\061', '\012', + '\170', '\115', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\121', '\150', '\155', '\040', '\164', '\150', '\040', + '\061', '\012', '\144', '\110', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\144', '\167', '\114', '\040', '\144', + '\145', '\040', '\061', '\012', '\166', '\131', '\162', '\040', + '\145', '\162', '\040', '\061', '\012', '\121', '\170', '\165', + '\040', '\161', '\165', '\040', '\061', '\012', '\144', '\116', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\172', + '\116', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\152', '\155', '\120', '\040', '\151', '\152', '\040', '\061', + '\012', '\120', '\142', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\146', '\161', '\152', '\040', '\161', '\165', + '\040', '\061', '\012', '\146', '\125', '\167', '\040', '\167', + '\141', '\040', '\061', '\012', '\110', '\171', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\121', '\144', '\170', + '\040', '\144', '\145', '\040', '\061', '\012', '\172', '\123', + '\154', '\040', '\154', '\145', '\040', '\061', '\012', '\143', + '\127', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\106', '\153', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\132', '\164', '\172', '\040', '\164', '\150', '\040', + '\061', '\012', '\165', '\125', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\156', '\102', '\155', '\040', '\141', + '\156', '\040', '\061', '\012', '\172', '\112', '\171', '\040', + '\163', '\172', '\040', '\061', '\012', '\160', '\144', '\111', + '\040', '\144', '\145', '\040', '\061', '\012', '\156', '\124', + '\144', '\040', '\141', '\156', '\040', '\061', '\012', '\131', + '\152', '\142', '\040', '\151', '\152', '\040', '\061', '\012', + '\121', '\152', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\130', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\170', '\167', '\102', '\040', '\157', '\167', + '\040', '\061', '\012', '\153', '\154', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\150', '\146', '\131', '\040', + '\164', '\150', '\040', '\061', '\012', '\160', '\104', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\172', '\132', + '\144', '\040', '\144', '\145', '\040', '\061', '\012', '\155', + '\161', '\117', '\040', '\161', '\165', '\040', '\061', '\012', + '\150', '\132', '\162', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\155', '\131', '\040', '\143', '\150', '\040', + '\061', '\012', '\147', '\114', '\153', '\040', '\156', '\147', + '\040', '\061', '\012', '\121', '\143', '\152', '\040', '\143', + '\150', '\040', '\061', '\012', '\165', '\113', '\152', '\040', + '\161', '\165', '\040', '\061', '\012', '\156', '\161', '\104', + '\040', '\141', '\156', '\040', '\061', '\012', '\171', '\113', + '\167', '\040', '\167', '\141', '\040', '\061', '\012', '\142', + '\146', '\122', '\040', '\142', '\145', '\040', '\061', '\012', + '\122', '\161', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\150', '\121', '\040', '\164', '\150', '\040', + '\061', '\012', '\166', '\116', '\152', '\040', '\151', '\152', + '\040', '\061', '\012', '\124', '\143', '\146', '\040', '\143', + '\150', '\040', '\061', '\012', '\110', '\142', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\114', '\167', '\166', + '\040', '\166', '\141', '\040', '\061', '\012', '\167', '\143', + '\132', '\040', '\143', '\150', '\040', '\061', '\012', '\143', + '\144', '\113', '\040', '\143', '\150', '\040', '\061', '\012', + '\142', '\160', '\122', '\040', '\160', '\162', '\040', '\061', + '\012', '\154', '\127', '\155', '\040', '\154', '\145', '\040', + '\061', '\012', '\167', '\116', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\160', '\101', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\147', '\162', '\126', '\040', + '\156', '\147', '\040', '\061', '\012', '\161', '\155', '\153', + '\040', '\161', '\165', '\040', '\061', '\012', '\143', '\114', + '\146', '\040', '\143', '\150', '\040', '\061', '\012', '\151', + '\167', '\102', '\040', '\151', '\156', '\040', '\061', '\012', + '\145', '\161', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\161', '\172', '\040', '\161', '\165', '\040', + '\061', '\012', '\121', '\156', '\152', '\040', '\141', '\156', + '\040', '\061', '\012', '\165', '\157', '\112', '\040', '\161', + '\165', '\040', '\061', '\012', '\146', '\126', '\152', '\040', + '\151', '\152', '\040', '\061', '\012', '\143', '\142', '\125', + '\040', '\143', '\150', '\040', '\061', '\012', '\161', '\160', + '\124', '\040', '\161', '\165', '\040', '\061', '\012', '\160', + '\144', '\132', '\040', '\144', '\145', '\040', '\061', '\012', + '\144', '\172', '\127', '\040', '\144', '\145', '\040', '\061', + '\012', '\127', '\146', '\167', '\040', '\167', '\141', '\040', + '\061', '\012', '\132', '\161', '\155', '\040', '\161', '\165', + '\040', '\061', '\012', '\153', '\112', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\172', '\127', '\146', '\040', + '\163', '\172', '\040', '\061', '\012', '\142', '\131', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\162', '\152', + '\121', '\040', '\145', '\162', '\040', '\061', '\012', '\144', + '\167', '\102', '\040', '\144', '\145', '\040', '\061', '\012', + '\126', '\154', '\170', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\113', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\114', '\170', '\167', '\040', '\167', '\141', + '\040', '\061', '\012', '\110', '\160', '\167', '\040', '\160', + '\162', '\040', '\061', '\012', '\155', '\166', '\122', '\040', + '\166', '\141', '\040', '\061', '\012', '\161', '\115', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\160', '\127', + '\142', '\040', '\160', '\162', '\040', '\061', '\012', '\144', + '\143', '\127', '\040', '\143', '\150', '\040', '\061', '\012', + '\172', '\105', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\162', '\163', '\040', '\145', '\162', '\040', + '\061', '\012', '\106', '\164', '\172', '\040', '\164', '\150', + '\040', '\061', '\012', '\161', '\171', '\114', '\040', '\161', + '\165', '\040', '\061', '\012', '\152', '\123', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\127', '\172', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\120', '\172', + '\146', '\040', '\163', '\172', '\040', '\061', '\012', '\172', + '\153', '\127', '\040', '\163', '\172', '\040', '\061', '\012', + '\171', '\167', '\131', '\040', '\167', '\141', '\040', '\061', + '\012', '\157', '\107', '\142', '\040', '\157', '\156', '\040', + '\061', '\012', '\152', '\102', '\167', '\040', '\151', '\152', + '\040', '\061', '\012', '\121', '\160', '\172', '\040', '\163', + '\172', '\040', '\061', '\012', '\162', '\127', '\155', '\040', + '\145', '\162', '\040', '\061', '\012', '\163', '\155', '\121', + '\040', '\163', '\164', '\040', '\061', '\012', '\165', '\107', + '\153', '\040', '\161', '\165', '\040', '\061', '\012', '\170', + '\153', '\126', '\040', '\153', '\141', '\040', '\061', '\012', + '\167', '\112', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\143', '\152', '\127', '\040', '\143', '\150', '\040', + '\061', '\012', '\167', '\116', '\170', '\040', '\167', '\141', + '\040', '\061', '\012', '\167', '\152', '\122', '\040', '\151', + '\152', '\040', '\061', '\012', '\167', '\104', '\144', '\040', + '\167', '\141', '\040', '\061', '\012', '\154', '\162', '\102', + '\040', '\145', '\162', '\040', '\061', '\012', '\161', '\150', + '\112', '\040', '\164', '\150', '\040', '\061', '\012', '\152', + '\113', '\160', '\040', '\151', '\152', '\040', '\061', '\012', + '\153', '\116', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\161', '\125', '\040', '\164', '\150', '\040', + '\061', '\012', '\112', '\155', '\152', '\040', '\151', '\152', + '\040', '\061', '\012', '\142', '\112', '\166', '\040', '\166', + '\141', '\040', '\061', '\012', '\146', '\162', '\116', '\040', + '\145', '\162', '\040', '\061', '\012', '\165', '\102', '\152', + '\040', '\161', '\165', '\040', '\061', '\012', '\125', '\165', + '\166', '\040', '\161', '\165', '\040', '\061', '\012', '\115', + '\172', '\166', '\040', '\163', '\172', '\040', '\061', '\012', + '\104', '\152', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\147', '\154', '\040', '\154', '\145', '\040', + '\061', '\012', '\150', '\144', '\103', '\040', '\164', '\150', + '\040', '\061', '\012', '\155', '\106', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\166', '\152', '\125', '\040', + '\151', '\152', '\040', '\061', '\012', '\160', '\162', '\130', + '\040', '\145', '\162', '\040', '\061', '\012', '\113', '\166', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\162', + '\171', '\131', '\040', '\145', '\162', '\040', '\061', '\012', + '\166', '\172', '\121', '\040', '\163', '\172', '\040', '\061', + '\012', '\117', '\152', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\121', '\146', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\126', '\161', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\141', '\121', '\166', '\040', + '\141', '\156', '\040', '\061', '\012', '\150', '\110', '\170', + '\040', '\164', '\150', '\040', '\061', '\012', '\165', '\111', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\113', + '\160', '\166', '\040', '\166', '\141', '\040', '\061', '\012', + '\144', '\121', '\153', '\040', '\153', '\157', '\040', '\061', + '\012', '\107', '\150', '\161', '\040', '\164', '\150', '\040', + '\061', '\012', '\143', '\132', '\163', '\040', '\143', '\150', + '\040', '\061', '\012', '\156', '\166', '\110', '\040', '\141', + '\156', '\040', '\061', '\012', '\152', '\167', '\112', '\040', + '\151', '\152', '\040', '\061', '\012', '\144', '\115', '\155', + '\040', '\144', '\145', '\040', '\061', '\012', '\147', '\152', + '\111', '\040', '\156', '\147', '\040', '\061', '\012', '\154', + '\120', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\161', '\102', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\126', '\150', '\161', '\040', '\164', '\150', '\040', + '\061', '\012', '\161', '\114', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\150', '\102', '\144', '\040', '\164', + '\150', '\040', '\061', '\012', '\126', '\143', '\165', '\040', + '\143', '\150', '\040', '\061', '\012', '\143', '\121', '\144', + '\040', '\143', '\150', '\040', '\061', '\012', '\171', '\160', + '\130', '\040', '\160', '\162', '\040', '\061', '\012', '\155', + '\121', '\166', '\040', '\166', '\141', '\040', '\061', '\012', + '\166', '\155', '\122', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\146', '\110', '\040', '\146', '\157', '\040', + '\061', '\012', '\160', '\161', '\131', '\040', '\161', '\165', + '\040', '\061', '\012', '\130', '\164', '\142', '\040', '\164', + '\150', '\040', '\061', '\012', '\126', '\143', '\170', '\040', + '\143', '\150', '\040', '\061', '\012', '\164', '\127', '\142', + '\040', '\164', '\150', '\040', '\061', '\012', '\120', '\170', + '\141', '\040', '\141', '\156', '\040', '\061', '\012', '\121', + '\155', '\162', '\040', '\145', '\162', '\040', '\061', '\012', + '\155', '\144', '\130', '\040', '\144', '\145', '\040', '\061', + '\012', '\102', '\170', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\152', '\132', '\166', '\040', '\151', '\152', + '\040', '\061', '\012', '\150', '\116', '\160', '\040', '\164', + '\150', '\040', '\061', '\012', '\171', '\142', '\116', '\040', + '\142', '\145', '\040', '\061', '\012', '\142', '\153', '\132', + '\040', '\153', '\141', '\040', '\061', '\012', '\156', '\126', + '\146', '\040', '\141', '\156', '\040', '\061', '\012', '\154', + '\113', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\157', '\112', '\152', '\040', '\157', '\156', '\040', '\061', + '\012', '\160', '\102', '\166', '\040', '\166', '\141', '\040', + '\061', '\012', '\150', '\147', '\101', '\040', '\164', '\150', + '\040', '\061', '\012', '\161', '\170', '\105', '\040', '\161', + '\165', '\040', '\061', '\012', '\156', '\166', '\112', '\040', + '\141', '\156', '\040', '\061', '\012', '\130', '\143', '\146', + '\040', '\143', '\150', '\040', '\061', '\012', '\106', '\144', + '\142', '\040', '\144', '\145', '\040', '\061', '\012', '\172', + '\101', '\157', '\040', '\157', '\156', '\040', '\061', '\012', + '\167', '\121', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\164', '\155', '\130', '\040', '\164', '\150', '\040', + '\061', '\012', '\160', '\166', '\132', '\040', '\166', '\141', + '\040', '\061', '\012', '\146', '\116', '\167', '\040', '\167', + '\141', '\040', '\061', '\012', '\172', '\113', '\153', '\040', + '\163', '\172', '\040', '\061', '\012', '\150', '\122', '\170', + '\040', '\164', '\150', '\040', '\061', '\012', '\124', '\154', + '\152', '\040', '\154', '\145', '\040', '\061', '\012', '\151', + '\121', '\152', '\040', '\151', '\156', '\040', '\061', '\012', + '\152', '\155', '\125', '\040', '\151', '\152', '\040', '\061', + '\012', '\164', '\142', '\127', '\040', '\164', '\150', '\040', + '\061', '\012', '\167', '\126', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\124', '\166', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\156', '\126', '\147', '\040', + '\141', '\156', '\040', '\061', '\012', '\114', '\170', '\160', + '\040', '\160', '\162', '\040', '\061', '\012', '\166', '\147', + '\117', '\040', '\156', '\147', '\040', '\061', '\012', '\144', + '\146', '\105', '\040', '\144', '\145', '\040', '\061', '\012', + '\156', '\126', '\155', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\113', '\171', '\040', '\161', '\165', '\040', + '\061', '\012', '\145', '\161', '\132', '\040', '\161', '\165', + '\040', '\061', '\012', '\124', '\143', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\143', '\124', '\153', '\040', + '\143', '\150', '\040', '\061', '\012', '\146', '\113', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\127', '\153', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\154', + '\166', '\132', '\040', '\154', '\145', '\040', '\061', '\012', + '\162', '\107', '\160', '\040', '\145', '\162', '\040', '\061', + '\012', '\153', '\113', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\103', '\142', '\146', '\040', '\142', '\145', + '\040', '\061', '\012', '\152', '\121', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\132', '\146', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\150', '\166', '\130', + '\040', '\164', '\150', '\040', '\061', '\012', '\170', '\147', + '\116', '\040', '\156', '\147', '\040', '\061', '\012', '\113', + '\160', '\145', '\040', '\145', '\162', '\040', '\061', '\012', + '\150', '\172', '\115', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\170', '\132', '\040', '\151', '\152', '\040', + '\061', '\012', '\171', '\161', '\114', '\040', '\161', '\165', + '\040', '\061', '\012', '\160', '\147', '\103', '\040', '\156', + '\147', '\040', '\061', '\012', '\106', '\161', '\144', '\040', + '\161', '\165', '\040', '\061', '\012', '\164', '\115', '\142', + '\040', '\164', '\150', '\040', '\061', '\012', '\156', '\152', + '\121', '\040', '\141', '\156', '\040', '\061', '\012', '\164', + '\146', '\102', '\040', '\164', '\150', '\040', '\061', '\012', + '\147', '\152', '\116', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\116', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\120', '\172', '\152', '\040', '\163', '\172', + '\040', '\061', '\012', '\155', '\150', '\117', '\040', '\164', + '\150', '\040', '\061', '\012', '\161', '\125', '\155', '\040', + '\161', '\165', '\040', '\061', '\012', '\106', '\150', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\123', '\152', + '\144', '\040', '\144', '\145', '\040', '\061', '\012', '\150', + '\127', '\152', '\040', '\164', '\150', '\040', '\061', '\012', + '\171', '\150', '\114', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\107', '\160', '\040', '\154', '\145', '\040', + '\061', '\012', '\144', '\164', '\130', '\040', '\164', '\150', + '\040', '\061', '\012', '\150', '\167', '\130', '\040', '\164', + '\150', '\040', '\061', '\012', '\163', '\162', '\113', '\040', + '\145', '\162', '\040', '\061', '\012', '\166', '\161', '\105', + '\040', '\161', '\165', '\040', '\061', '\012', '\142', '\143', + '\117', '\040', '\143', '\150', '\040', '\061', '\012', '\170', + '\121', '\154', '\040', '\154', '\145', '\040', '\061', '\012', + '\121', '\161', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\112', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\160', '\130', '\172', '\040', '\163', '\172', + '\040', '\061', '\012', '\171', '\165', '\112', '\040', '\161', + '\165', '\040', '\061', '\012', '\107', '\156', '\160', '\040', + '\141', '\156', '\040', '\061', '\012', '\104', '\154', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\115', '\170', + '\146', '\040', '\146', '\157', '\040', '\061', '\012', '\171', + '\116', '\162', '\040', '\145', '\162', '\040', '\061', '\012', + '\142', '\155', '\126', '\040', '\155', '\145', '\040', '\061', + '\012', '\146', '\130', '\157', '\040', '\157', '\156', '\040', + '\061', '\012', '\155', '\167', '\127', '\040', '\155', '\145', + '\040', '\061', '\012', '\154', '\111', '\152', '\040', '\154', + '\145', '\040', '\061', '\012', '\106', '\166', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\125', '\164', '\161', + '\040', '\164', '\150', '\040', '\061', '\012', '\152', '\107', + '\153', '\040', '\151', '\152', '\040', '\061', '\012', '\167', + '\131', '\167', '\040', '\167', '\141', '\040', '\061', '\012', + '\167', '\126', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\142', '\124', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\111', '\152', '\160', '\040', '\151', '\152', + '\040', '\061', '\012', '\172', '\156', '\115', '\040', '\141', + '\156', '\040', '\061', '\012', '\170', '\155', '\117', '\040', + '\155', '\145', '\040', '\061', '\012', '\147', '\121', '\170', + '\040', '\156', '\147', '\040', '\061', '\012', '\144', '\113', + '\167', '\040', '\144', '\145', '\040', '\061', '\012', '\144', + '\125', '\146', '\040', '\144', '\145', '\040', '\061', '\012', + '\143', '\123', '\142', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\126', '\142', '\040', '\163', '\172', '\040', + '\061', '\012', '\143', '\143', '\131', '\040', '\143', '\150', + '\040', '\061', '\012', '\170', '\152', '\105', '\040', '\151', + '\152', '\040', '\061', '\012', '\160', '\131', '\164', '\040', + '\164', '\150', '\040', '\061', '\012', '\126', '\162', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\153', '\172', + '\113', '\040', '\163', '\172', '\040', '\061', '\012', '\172', + '\146', '\103', '\040', '\163', '\172', '\040', '\061', '\012', + '\131', '\142', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\147', '\123', '\040', '\156', '\147', '\040', + '\061', '\012', '\170', '\143', '\126', '\040', '\143', '\150', + '\040', '\061', '\012', '\170', '\116', '\155', '\040', '\155', + '\145', '\040', '\061', '\012', '\130', '\153', '\167', '\040', + '\153', '\141', '\040', '\061', '\012', '\124', '\160', '\167', + '\040', '\160', '\162', '\040', '\061', '\012', '\102', '\167', + '\144', '\040', '\144', '\145', '\040', '\061', '\012', '\150', + '\167', '\124', '\040', '\164', '\150', '\040', '\061', '\012', + '\147', '\121', '\154', '\040', '\156', '\147', '\040', '\061', + '\012', '\143', '\104', '\163', '\040', '\143', '\150', '\040', + '\061', '\012', '\172', '\131', '\162', '\040', '\145', '\162', + '\040', '\061', '\012', '\170', '\124', '\160', '\040', '\160', + '\162', '\040', '\061', '\012', '\161', '\127', '\155', '\040', + '\161', '\165', '\040', '\061', '\012', '\170', '\152', '\124', + '\040', '\151', '\152', '\040', '\061', '\012', '\150', '\152', + '\113', '\040', '\164', '\150', '\040', '\061', '\012', '\165', + '\104', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\170', '\150', '\123', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\127', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\166', '\103', '\167', '\040', '\166', '\141', + '\040', '\061', '\012', '\152', '\171', '\102', '\040', '\151', + '\152', '\040', '\061', '\012', '\165', '\127', '\144', '\040', + '\161', '\165', '\040', '\061', '\012', '\116', '\156', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\121', '\166', + '\142', '\040', '\166', '\141', '\040', '\061', '\012', '\152', + '\172', '\126', '\040', '\163', '\172', '\040', '\061', '\012', + '\172', '\102', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\111', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\161', '\122', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\161', '\162', '\112', '\040', '\161', + '\165', '\040', '\061', '\012', '\172', '\132', '\152', '\040', + '\163', '\172', '\040', '\061', '\012', '\153', '\122', '\162', + '\040', '\145', '\162', '\040', '\061', '\012', '\116', '\172', + '\166', '\040', '\163', '\172', '\040', '\061', '\012', '\121', + '\146', '\167', '\040', '\167', '\141', '\040', '\061', '\012', + '\116', '\152', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\106', '\171', '\040', '\142', '\145', '\040', + '\061', '\012', '\154', '\150', '\131', '\040', '\164', '\150', + '\040', '\061', '\012', '\145', '\127', '\152', '\040', '\145', + '\162', '\040', '\061', '\012', '\152', '\142', '\115', '\040', + '\151', '\152', '\040', '\061', '\012', '\130', '\163', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\122', '\163', + '\144', '\040', '\144', '\145', '\040', '\061', '\012', '\146', + '\154', '\106', '\040', '\154', '\145', '\040', '\061', '\012', + '\120', '\150', '\172', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\127', '\163', '\040', '\163', '\164', '\040', + '\061', '\012', '\142', '\103', '\167', '\040', '\167', '\141', + '\040', '\061', '\012', '\147', '\146', '\112', '\040', '\156', + '\147', '\040', '\061', '\012', '\161', '\126', '\157', '\040', + '\161', '\165', '\040', '\061', '\012', '\145', '\121', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\166', '\143', + '\120', '\040', '\143', '\150', '\040', '\061', '\012', '\155', + '\104', '\152', '\040', '\151', '\152', '\040', '\061', '\012', + '\161', '\124', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\147', '\163', '\040', '\156', '\147', '\040', + '\061', '\012', '\126', '\165', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\165', '\146', '\116', '\040', '\161', + '\165', '\040', '\061', '\012', '\170', '\102', '\163', '\040', + '\163', '\164', '\040', '\061', '\012', '\160', '\124', '\153', + '\040', '\153', '\141', '\040', '\061', '\012', '\146', '\123', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\155', + '\142', '\104', '\040', '\155', '\145', '\040', '\061', '\012', + '\126', '\167', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\150', '\121', '\040', '\164', '\150', '\040', + '\061', '\012', '\153', '\146', '\120', '\040', '\153', '\141', + '\040', '\061', '\012', '\120', '\167', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\144', '\150', '\107', '\040', + '\164', '\150', '\040', '\061', '\012', '\161', '\132', '\152', + '\040', '\161', '\165', '\040', '\061', '\012', '\171', '\122', + '\152', '\040', '\151', '\152', '\040', '\061', '\012', '\171', + '\103', '\163', '\040', '\163', '\164', '\040', '\061', '\012', + '\146', '\152', '\116', '\040', '\151', '\152', '\040', '\061', + '\012', '\122', '\161', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\152', '\112', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\144', '\154', '\122', '\040', '\154', + '\145', '\040', '\061', '\012', '\130', '\155', '\142', '\040', + '\155', '\145', '\040', '\061', '\012', '\112', '\152', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\147', '\161', + '\111', '\040', '\156', '\147', '\040', '\061', '\012', '\146', + '\161', '\115', '\040', '\161', '\165', '\040', '\061', '\012', + '\151', '\126', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\110', '\147', '\165', '\040', '\156', '\147', '\040', + '\061', '\012', '\151', '\110', '\167', '\040', '\151', '\156', + '\040', '\061', '\012', '\145', '\121', '\166', '\040', '\145', + '\162', '\040', '\061', '\012', '\155', '\172', '\105', '\040', + '\163', '\172', '\040', '\061', '\012', '\146', '\152', '\132', + '\040', '\151', '\152', '\040', '\061', '\012', '\161', '\116', + '\156', '\040', '\141', '\156', '\040', '\061', '\012', '\167', + '\154', '\105', '\040', '\154', '\145', '\040', '\061', '\012', + '\153', '\107', '\160', '\040', '\153', '\141', '\040', '\061', + '\012', '\111', '\161', '\166', '\040', '\161', '\165', '\040', + '\061', '\012', '\153', '\102', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\170', '\132', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\104', '\153', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\172', '\154', '\110', + '\040', '\154', '\145', '\040', '\061', '\012', '\164', '\170', + '\102', '\040', '\164', '\150', '\040', '\061', '\012', '\164', + '\121', '\162', '\040', '\164', '\150', '\040', '\061', '\012', + '\165', '\117', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\112', '\151', '\040', '\151', '\156', '\040', + '\061', '\012', '\172', '\142', '\114', '\040', '\163', '\172', + '\040', '\061', '\012', '\170', '\153', '\104', '\040', '\153', + '\141', '\040', '\061', '\012', '\163', '\143', '\126', '\040', + '\143', '\150', '\040', '\061', '\012', '\161', '\130', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\153', '\111', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\170', + '\116', '\156', '\040', '\141', '\156', '\040', '\061', '\012', + '\147', '\112', '\146', '\040', '\156', '\147', '\040', '\061', + '\012', '\164', '\155', '\102', '\040', '\164', '\150', '\040', + '\061', '\012', '\164', '\143', '\113', '\040', '\164', '\150', + '\040', '\061', '\012', '\153', '\167', '\132', '\040', '\153', + '\141', '\040', '\061', '\012', '\165', '\132', '\152', '\040', + '\161', '\165', '\040', '\061', '\012', '\163', '\156', '\121', + '\040', '\141', '\156', '\040', '\061', '\012', '\165', '\113', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\143', + '\162', '\130', '\040', '\143', '\150', '\040', '\061', '\012', + '\150', '\130', '\171', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\143', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\120', '\146', '\172', '\040', '\163', '\172', + '\040', '\061', '\012', '\144', '\167', '\115', '\040', '\144', + '\145', '\040', '\061', '\012', '\161', '\111', '\171', '\040', + '\161', '\165', '\040', '\061', '\012', '\170', '\165', '\120', + '\040', '\161', '\165', '\040', '\061', '\012', '\167', '\104', + '\167', '\040', '\167', '\141', '\040', '\061', '\012', '\110', + '\152', '\162', '\040', '\145', '\162', '\040', '\061', '\012', + '\144', '\121', '\146', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\166', '\112', '\040', '\167', '\141', '\040', + '\061', '\012', '\164', '\110', '\155', '\040', '\164', '\150', + '\040', '\061', '\012', '\131', '\144', '\167', '\040', '\144', + '\145', '\040', '\061', '\012', '\167', '\170', '\111', '\040', + '\167', '\141', '\040', '\061', '\012', '\160', '\117', '\166', + '\040', '\166', '\141', '\040', '\061', '\012', '\127', '\155', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\144', + '\150', '\104', '\040', '\164', '\150', '\040', '\061', '\012', + '\161', '\160', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\155', '\103', '\040', '\155', '\145', '\040', + '\061', '\012', '\167', '\143', '\130', '\040', '\143', '\150', + '\040', '\061', '\012', '\167', '\152', '\110', '\040', '\151', + '\152', '\040', '\061', '\012', '\142', '\127', '\146', '\040', + '\142', '\145', '\040', '\061', '\012', '\107', '\144', '\160', + '\040', '\144', '\145', '\040', '\061', '\012', '\114', '\144', + '\167', '\040', '\144', '\145', '\040', '\061', '\012', '\123', + '\142', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\166', '\132', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\113', '\167', '\142', '\040', '\167', '\141', '\040', + '\061', '\012', '\161', '\150', '\124', '\040', '\164', '\150', + '\040', '\061', '\012', '\171', '\122', '\146', '\040', '\156', + '\171', '\040', '\061', '\012', '\150', '\167', '\103', '\040', + '\164', '\150', '\040', '\061', '\012', '\156', '\160', '\112', + '\040', '\141', '\156', '\040', '\061', '\012', '\152', '\155', + '\126', '\040', '\151', '\152', '\040', '\061', '\012', '\166', + '\107', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\170', '\161', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\120', '\150', '\155', '\040', '\164', '\150', '\040', + '\061', '\012', '\160', '\127', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\126', '\170', '\153', '\040', '\153', + '\141', '\040', '\061', '\012', '\163', '\110', '\172', '\040', + '\163', '\164', '\040', '\061', '\012', '\127', '\142', '\170', + '\040', '\142', '\145', '\040', '\061', '\012', '\142', '\146', + '\113', '\040', '\142', '\145', '\040', '\061', '\012', '\112', + '\147', '\154', '\040', '\156', '\147', '\040', '\061', '\012', + '\153', '\124', '\142', '\040', '\153', '\141', '\040', '\061', + '\012', '\113', '\142', '\146', '\040', '\142', '\145', '\040', + '\061', '\012', '\153', '\172', '\103', '\040', '\163', '\172', + '\040', '\061', '\012', '\160', '\113', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\172', '\167', '\102', '\040', + '\163', '\172', '\040', '\061', '\012', '\165', '\132', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\142', '\164', + '\111', '\040', '\164', '\150', '\040', '\061', '\012', '\172', + '\130', '\152', '\040', '\163', '\172', '\040', '\061', '\012', + '\165', '\172', '\123', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\127', '\153', '\040', '\153', '\141', '\040', + '\061', '\012', '\170', '\162', '\110', '\040', '\145', '\162', + '\040', '\061', '\012', '\157', '\121', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\172', '\154', '\124', '\040', + '\154', '\145', '\040', '\061', '\012', '\144', '\146', '\111', + '\040', '\144', '\145', '\040', '\061', '\012', '\121', '\155', + '\146', '\040', '\155', '\145', '\040', '\061', '\012', '\163', + '\147', '\105', '\040', '\156', '\147', '\040', '\061', '\012', + '\131', '\163', '\170', '\040', '\163', '\164', '\040', '\061', + '\012', '\122', '\172', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\170', '\114', '\144', '\040', '\144', '\145', + '\040', '\061', '\012', '\161', '\163', '\130', '\040', '\161', + '\165', '\040', '\061', '\012', '\153', '\161', '\112', '\040', + '\161', '\165', '\040', '\061', '\012', '\153', '\103', '\155', + '\040', '\153', '\141', '\040', '\061', '\012', '\142', '\106', + '\155', '\040', '\155', '\145', '\040', '\061', '\012', '\151', + '\147', '\121', '\040', '\156', '\147', '\040', '\061', '\012', + '\163', '\122', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\107', '\155', '\040', '\151', '\152', '\040', + '\061', '\012', '\123', '\172', '\163', '\040', '\163', '\164', + '\040', '\061', '\012', '\131', '\166', '\172', '\040', '\163', + '\172', '\040', '\061', '\012', '\153', '\130', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\107', '\156', '\172', + '\040', '\141', '\156', '\040', '\061', '\012', '\155', '\127', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\164', + '\104', '\161', '\040', '\164', '\150', '\040', '\061', '\012', + '\147', '\161', '\172', '\040', '\156', '\147', '\040', '\061', + '\012', '\156', '\110', '\142', '\040', '\156', '\147', '\040', + '\061', '\012', '\164', '\144', '\115', '\040', '\164', '\150', + '\040', '\061', '\012', '\117', '\166', '\170', '\040', '\166', + '\141', '\040', '\061', '\012', '\132', '\156', '\154', '\040', + '\141', '\156', '\040', '\061', '\012', '\167', '\165', '\105', + '\040', '\161', '\165', '\040', '\061', '\012', '\172', '\114', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\157', + '\146', '\121', '\040', '\157', '\156', '\040', '\061', '\012', + '\166', '\131', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\171', '\110', '\040', '\151', '\152', '\040', + '\061', '\012', '\172', '\161', '\101', '\040', '\161', '\165', + '\040', '\061', '\012', '\143', '\112', '\171', '\040', '\143', + '\150', '\040', '\061', '\012', '\127', '\142', '\146', '\040', + '\142', '\145', '\040', '\061', '\012', '\154', '\124', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\153', '\154', + '\127', '\040', '\154', '\145', '\040', '\061', '\012', '\130', + '\170', '\141', '\040', '\141', '\156', '\040', '\061', '\012', + '\146', '\103', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\113', '\146', '\040', '\154', '\145', '\040', + '\061', '\012', '\161', '\167', '\124', '\040', '\161', '\165', + '\040', '\061', '\012', '\162', '\110', '\153', '\040', '\145', + '\162', '\040', '\061', '\012', '\144', '\142', '\116', '\040', + '\144', '\145', '\040', '\061', '\012', '\165', '\125', '\171', + '\040', '\161', '\165', '\040', '\061', '\012', '\172', '\147', + '\116', '\040', '\156', '\147', '\040', '\061', '\012', '\120', + '\170', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\160', '\116', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\171', '\112', '\040', '\143', '\150', '\040', + '\061', '\012', '\152', '\160', '\110', '\040', '\151', '\152', + '\040', '\061', '\012', '\126', '\164', '\146', '\040', '\164', + '\150', '\040', '\061', '\012', '\163', '\152', '\112', '\040', + '\163', '\164', '\040', '\061', '\012', '\121', '\154', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\164', '\167', + '\126', '\040', '\164', '\150', '\040', '\061', '\012', '\171', + '\107', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\164', '\126', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\163', '\121', '\040', '\163', '\164', '\040', + '\061', '\012', '\170', '\156', '\124', '\040', '\141', '\156', + '\040', '\061', '\012', '\162', '\160', '\112', '\040', '\145', + '\162', '\040', '\061', '\012', '\167', '\172', '\111', '\040', + '\163', '\172', '\040', '\061', '\012', '\132', '\150', '\160', + '\040', '\164', '\150', '\040', '\061', '\012', '\141', '\104', + '\146', '\040', '\141', '\156', '\040', '\061', '\012', '\125', + '\170', '\152', '\040', '\151', '\152', '\040', '\061', '\012', + '\143', '\120', '\147', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\123', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\155', '\113', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\166', '\102', '\172', '\040', '\163', + '\172', '\040', '\061', '\012', '\171', '\120', '\152', '\040', + '\151', '\152', '\040', '\061', '\012', '\126', '\153', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\161', '\151', + '\102', '\040', '\161', '\165', '\040', '\061', '\012', '\164', + '\153', '\112', '\040', '\164', '\150', '\040', '\061', '\012', + '\117', '\165', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\157', '\110', '\040', '\157', '\156', '\040', + '\061', '\012', '\161', '\126', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\107', '\170', '\163', '\040', '\163', + '\164', '\040', '\061', '\012', '\152', '\172', '\106', '\040', + '\163', '\172', '\040', '\061', '\012', '\163', '\167', '\110', + '\040', '\163', '\164', '\040', '\061', '\012', '\156', '\102', + '\142', '\040', '\141', '\156', '\040', '\061', '\012', '\172', + '\150', '\121', '\040', '\164', '\150', '\040', '\061', '\012', + '\171', '\122', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\146', '\156', '\130', '\040', '\141', '\156', '\040', + '\061', '\012', '\161', '\157', '\121', '\040', '\161', '\165', + '\040', '\061', '\012', '\155', '\170', '\120', '\040', '\155', + '\145', '\040', '\061', '\012', '\142', '\167', '\122', '\040', + '\167', '\141', '\040', '\061', '\012', '\147', '\112', '\152', + '\040', '\156', '\147', '\040', '\061', '\012', '\161', '\156', + '\153', '\040', '\141', '\156', '\040', '\061', '\012', '\164', + '\115', '\153', '\040', '\164', '\150', '\040', '\061', '\012', + '\144', '\170', '\117', '\040', '\144', '\145', '\040', '\061', + '\012', '\162', '\172', '\126', '\040', '\145', '\162', '\040', + '\061', '\012', '\166', '\160', '\120', '\040', '\166', '\141', + '\040', '\061', '\012', '\116', '\166', '\172', '\040', '\163', + '\172', '\040', '\061', '\012', '\116', '\146', '\160', '\040', + '\160', '\162', '\040', '\061', '\012', '\103', '\156', '\172', + '\040', '\141', '\156', '\040', '\061', '\012', '\157', '\124', + '\144', '\040', '\157', '\156', '\040', '\061', '\012', '\144', + '\161', '\107', '\040', '\161', '\165', '\040', '\061', '\012', + '\110', '\155', '\170', '\040', '\155', '\145', '\040', '\061', + '\012', '\160', '\163', '\130', '\040', '\163', '\164', '\040', + '\061', '\012', '\163', '\167', '\115', '\040', '\163', '\164', + '\040', '\061', '\012', '\144', '\161', '\103', '\040', '\161', + '\165', '\040', '\061', '\012', '\126', '\167', '\170', '\040', + '\167', '\141', '\040', '\061', '\012', '\156', '\130', '\146', + '\040', '\141', '\156', '\040', '\061', '\012', '\167', '\153', + '\131', '\040', '\153', '\141', '\040', '\061', '\012', '\167', + '\146', '\103', '\040', '\167', '\141', '\040', '\061', '\012', + '\161', '\123', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\126', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\153', '\104', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\131', '\166', '\142', '\040', '\166', + '\141', '\040', '\061', '\012', '\172', '\161', '\110', '\040', + '\161', '\165', '\040', '\061', '\012', '\161', '\170', '\112', + '\040', '\161', '\165', '\040', '\061', '\012', '\172', '\113', + '\152', '\040', '\163', '\172', '\040', '\061', '\012', '\152', + '\143', '\116', '\040', '\143', '\150', '\040', '\061', '\012', + '\164', '\127', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\122', '\162', '\172', '\040', '\145', '\162', '\040', + '\061', '\012', '\142', '\155', '\107', '\040', '\155', '\145', + '\040', '\061', '\012', '\163', '\162', '\132', '\040', '\145', + '\162', '\040', '\061', '\012', '\167', '\127', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\103', '\146', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\154', '\116', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\150', + '\143', '\126', '\040', '\164', '\150', '\040', '\061', '\012', + '\132', '\156', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\112', '\150', '\166', '\040', '\164', '\150', '\040', + '\061', '\012', '\161', '\111', '\160', '\040', '\161', '\165', + '\040', '\061', '\012', '\166', '\123', '\172', '\040', '\163', + '\172', '\040', '\061', '\012', '\146', '\145', '\125', '\040', + '\145', '\162', '\040', '\061', '\012', '\170', '\111', '\151', + '\040', '\151', '\156', '\040', '\061', '\012', '\132', '\155', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\145', + '\107', '\146', '\040', '\145', '\162', '\040', '\061', '\012', + '\142', '\121', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\130', '\143', '\142', '\040', '\143', '\150', '\040', + '\061', '\012', '\156', '\154', '\113', '\040', '\141', '\156', + '\040', '\061', '\012', '\164', '\155', '\112', '\040', '\164', + '\150', '\040', '\061', '\012', '\152', '\154', '\114', '\040', + '\154', '\145', '\040', '\061', '\012', '\155', '\167', '\103', + '\040', '\155', '\145', '\040', '\061', '\012', '\161', '\152', + '\162', '\040', '\161', '\165', '\040', '\061', '\012', '\172', + '\102', '\142', '\040', '\163', '\172', '\040', '\061', '\012', + '\146', '\150', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\120', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\163', '\102', '\146', '\040', '\163', '\164', + '\040', '\061', '\012', '\165', '\130', '\171', '\040', '\161', + '\165', '\040', '\061', '\012', '\114', '\153', '\170', '\040', + '\153', '\141', '\040', '\061', '\012', '\162', '\107', '\172', + '\040', '\145', '\162', '\040', '\061', '\012', '\150', '\130', + '\172', '\040', '\164', '\150', '\040', '\061', '\012', '\172', + '\165', '\127', '\040', '\161', '\165', '\040', '\061', '\012', + '\122', '\166', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\142', '\143', '\112', '\040', '\143', '\150', '\040', + '\061', '\012', '\105', '\157', '\152', '\040', '\157', '\156', + '\040', '\061', '\012', '\151', '\126', '\164', '\040', '\151', + '\156', '\040', '\061', '\012', '\171', '\150', '\110', '\040', + '\164', '\150', '\040', '\061', '\012', '\170', '\126', '\166', + '\040', '\166', '\141', '\040', '\061', '\012', '\160', '\115', + '\162', '\040', '\145', '\162', '\040', '\061', '\012', '\166', + '\132', '\144', '\040', '\144', '\145', '\040', '\061', '\012', + '\126', '\166', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\151', '\103', '\166', '\040', '\151', '\156', '\040', + '\061', '\012', '\166', '\121', '\160', '\040', '\166', '\141', + '\040', '\061', '\012', '\166', '\154', '\102', '\040', '\154', + '\145', '\040', '\061', '\012', '\167', '\126', '\164', '\040', + '\164', '\150', '\040', '\061', '\012', '\125', '\147', '\153', + '\040', '\156', '\147', '\040', '\061', '\012', '\153', '\164', + '\121', '\040', '\164', '\150', '\040', '\061', '\012', '\152', + '\103', '\162', '\040', '\145', '\162', '\040', '\061', '\012', + '\161', '\166', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\126', '\146', '\040', '\142', '\145', '\040', + '\061', '\012', '\162', '\120', '\166', '\040', '\145', '\162', + '\040', '\061', '\012', '\167', '\146', '\110', '\040', '\167', + '\141', '\040', '\061', '\012', '\150', '\142', '\125', '\040', + '\164', '\150', '\040', '\061', '\012', '\160', '\152', '\106', + '\040', '\151', '\152', '\040', '\061', '\012', '\157', '\130', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\172', + '\123', '\162', '\040', '\145', '\162', '\040', '\061', '\012', + '\167', '\122', '\142', '\040', '\167', '\141', '\040', '\061', + '\012', '\110', '\143', '\165', '\040', '\143', '\150', '\040', + '\061', '\012', '\171', '\170', '\112', '\040', '\156', '\171', + '\040', '\061', '\012', '\154', '\124', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\142', '\131', '\142', '\040', + '\142', '\145', '\040', '\061', '\012', '\127', '\170', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\166', '\162', + '\105', '\040', '\145', '\162', '\040', '\061', '\012', '\172', + '\107', '\171', '\040', '\163', '\172', '\040', '\061', '\012', + '\112', '\161', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\172', '\111', '\040', '\145', '\162', '\040', + '\061', '\012', '\170', '\147', '\126', '\040', '\147', '\151', + '\040', '\061', '\012', '\122', '\166', '\167', '\040', '\166', + '\141', '\040', '\061', '\012', '\126', '\156', '\170', '\040', + '\141', '\156', '\040', '\061', '\012', '\165', '\112', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\150', '\106', + '\161', '\040', '\164', '\150', '\040', '\061', '\012', '\124', + '\147', '\172', '\040', '\156', '\147', '\040', '\061', '\012', + '\141', '\121', '\143', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\172', '\112', '\040', '\163', '\172', '\040', + '\061', '\012', '\164', '\116', '\143', '\040', '\164', '\150', + '\040', '\061', '\012', '\152', '\146', '\101', '\040', '\151', + '\152', '\040', '\061', '\012', '\171', '\143', '\117', '\040', + '\143', '\150', '\040', '\061', '\012', '\127', '\153', '\152', + '\040', '\151', '\152', '\040', '\061', '\012', '\171', '\102', + '\160', '\040', '\160', '\162', '\040', '\061', '\012', '\150', + '\147', '\104', '\040', '\164', '\150', '\040', '\061', '\012', + '\151', '\123', '\170', '\040', '\151', '\156', '\040', '\061', + '\012', '\170', '\103', '\155', '\040', '\155', '\145', '\040', + '\061', '\012', '\171', '\152', '\130', '\040', '\151', '\152', + '\040', '\061', '\012', '\165', '\111', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\161', '\147', '\161', '\040', + '\156', '\147', '\040', '\061', '\012', '\124', '\172', '\152', + '\040', '\163', '\172', '\040', '\061', '\012', '\171', '\152', + '\117', '\040', '\151', '\152', '\040', '\061', '\012', '\171', + '\162', '\131', '\040', '\145', '\162', '\040', '\061', '\012', + '\142', '\155', '\132', '\040', '\155', '\145', '\040', '\061', + '\012', '\172', '\161', '\124', '\040', '\161', '\165', '\040', + '\061', '\012', '\155', '\102', '\144', '\040', '\144', '\145', + '\040', '\061', '\012', '\161', '\166', '\113', '\040', '\161', + '\165', '\040', '\061', '\012', '\172', '\143', '\101', '\040', + '\143', '\150', '\040', '\061', '\012', '\170', '\162', '\130', + '\040', '\145', '\162', '\040', '\061', '\012', '\155', '\112', + '\155', '\040', '\155', '\145', '\040', '\061', '\012', '\130', + '\161', '\146', '\040', '\161', '\165', '\040', '\061', '\012', + '\120', '\170', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\141', '\104', '\142', '\040', '\141', '\156', '\040', + '\061', '\012', '\161', '\130', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\145', '\107', '\167', '\040', '\145', + '\162', '\040', '\061', '\012', '\150', '\152', '\104', '\040', + '\164', '\150', '\040', '\061', '\012', '\164', '\124', '\170', + '\040', '\164', '\150', '\040', '\061', '\012', '\157', '\115', + '\144', '\040', '\157', '\156', '\040', '\061', '\012', '\146', + '\113', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\116', '\160', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\161', '\125', '\040', '\161', '\165', '\040', + '\061', '\012', '\154', '\142', '\106', '\040', '\154', '\145', + '\040', '\061', '\012', '\110', '\166', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\161', '\132', '\145', '\040', + '\161', '\165', '\040', '\061', '\012', '\154', '\121', '\152', + '\040', '\154', '\145', '\040', '\061', '\012', '\144', '\153', + '\131', '\040', '\144', '\145', '\040', '\061', '\012', '\144', + '\132', '\154', '\040', '\154', '\145', '\040', '\061', '\012', + '\172', '\132', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\171', '\115', '\040', '\161', '\165', '\040', + '\061', '\012', '\144', '\155', '\112', '\040', '\144', '\145', + '\040', '\061', '\012', '\153', '\146', '\113', '\040', '\153', + '\141', '\040', '\061', '\012', '\151', '\120', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\172', '\167', '\125', + '\040', '\163', '\172', '\040', '\061', '\012', '\160', '\166', + '\123', '\040', '\166', '\141', '\040', '\061', '\012', '\151', + '\150', '\112', '\040', '\164', '\150', '\040', '\061', '\012', + '\165', '\143', '\127', '\040', '\143', '\150', '\040', '\061', + '\012', '\112', '\152', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\155', '\115', '\144', '\040', '\144', '\145', + '\040', '\061', '\012', '\166', '\160', '\167', '\040', '\166', + '\141', '\040', '\061', '\012', '\170', '\103', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\150', '\113', '\163', + '\040', '\164', '\150', '\040', '\061', '\012', '\166', '\154', + '\111', '\040', '\154', '\145', '\040', '\061', '\012', '\116', + '\155', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\170', '\172', '\126', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\132', '\163', '\040', '\156', '\147', '\040', + '\061', '\012', '\162', '\122', '\160', '\040', '\145', '\162', + '\040', '\061', '\012', '\125', '\146', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\146', '\160', '\106', '\040', + '\160', '\162', '\040', '\061', '\012', '\146', '\167', '\131', + '\040', '\167', '\141', '\040', '\061', '\012', '\107', '\170', + '\162', '\040', '\145', '\162', '\040', '\061', '\012', '\170', + '\114', '\162', '\040', '\145', '\162', '\040', '\061', '\012', + '\166', '\172', '\105', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\122', '\146', '\040', '\151', '\152', '\040', + '\061', '\012', '\142', '\162', '\122', '\040', '\145', '\162', + '\040', '\061', '\012', '\147', '\153', '\132', '\040', '\156', + '\147', '\040', '\061', '\012', '\144', '\125', '\171', '\040', + '\144', '\145', '\040', '\061', '\012', '\130', '\152', '\151', + '\040', '\151', '\156', '\040', '\061', '\012', '\113', '\144', + '\142', '\040', '\144', '\145', '\040', '\061', '\012', '\152', + '\160', '\103', '\040', '\151', '\152', '\040', '\061', '\012', + '\157', '\125', '\152', '\040', '\157', '\156', '\040', '\061', + '\012', '\161', '\155', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\161', '\152', '\114', '\040', '\161', '\165', + '\040', '\061', '\012', '\167', '\122', '\163', '\040', '\163', + '\172', '\040', '\061', '\012', '\152', '\150', '\115', '\040', + '\164', '\150', '\040', '\061', '\012', '\122', '\150', '\162', + '\040', '\164', '\150', '\040', '\061', '\012', '\142', '\164', + '\116', '\040', '\164', '\150', '\040', '\061', '\012', '\120', + '\152', '\161', '\040', '\151', '\152', '\040', '\061', '\012', + '\170', '\167', '\125', '\040', '\167', '\141', '\040', '\061', + '\012', '\161', '\171', '\105', '\040', '\161', '\165', '\040', + '\061', '\012', '\112', '\170', '\144', '\040', '\144', '\145', + '\040', '\061', '\012', '\120', '\161', '\162', '\040', '\161', + '\165', '\040', '\061', '\012', '\154', '\122', '\144', '\040', + '\154', '\145', '\040', '\061', '\012', '\152', '\161', '\111', + '\040', '\161', '\165', '\040', '\061', '\012', '\161', '\106', + '\163', '\040', '\161', '\165', '\040', '\061', '\012', '\115', + '\167', '\153', '\040', '\153', '\141', '\040', '\061', '\012', + '\152', '\105', '\142', '\040', '\151', '\152', '\040', '\061', + '\012', '\116', '\170', '\171', '\040', '\156', '\171', '\040', + '\061', '\012', '\120', '\172', '\155', '\040', '\163', '\172', + '\040', '\061', '\012', '\164', '\146', '\114', '\040', '\164', + '\150', '\040', '\061', '\012', '\166', '\106', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\152', '\121', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\102', '\156', + '\170', '\040', '\141', '\156', '\040', '\061', '\012', '\154', + '\115', '\166', '\040', '\154', '\145', '\040', '\061', '\012', + '\164', '\113', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\145', '\126', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\124', '\171', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\144', '\162', '\112', '\040', '\145', + '\162', '\040', '\061', '\012', '\157', '\110', '\167', '\040', + '\157', '\156', '\040', '\061', '\012', '\154', '\106', '\153', + '\040', '\154', '\145', '\040', '\061', '\012', '\152', '\160', + '\127', '\040', '\151', '\152', '\040', '\061', '\012', '\121', + '\152', '\167', '\040', '\151', '\152', '\040', '\061', '\012', + '\143', '\116', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\102', '\150', '\172', '\040', '\164', '\150', '\040', + '\061', '\012', '\142', '\150', '\102', '\040', '\164', '\150', + '\040', '\061', '\012', '\160', '\104', '\170', '\040', '\160', + '\162', '\040', '\061', '\012', '\170', '\160', '\131', '\040', + '\160', '\162', '\040', '\061', '\012', '\164', '\156', '\110', + '\040', '\164', '\150', '\040', '\061', '\012', '\144', '\146', + '\114', '\040', '\144', '\145', '\040', '\061', '\012', '\150', + '\172', '\114', '\040', '\164', '\150', '\040', '\061', '\012', + '\172', '\116', '\153', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\102', '\155', '\040', '\154', '\145', '\040', + '\061', '\012', '\154', '\130', '\154', '\040', '\154', '\145', + '\040', '\061', '\012', '\171', '\120', '\166', '\040', '\166', + '\141', '\040', '\061', '\012', '\132', '\143', '\154', '\040', + '\143', '\150', '\040', '\061', '\012', '\150', '\115', '\161', + '\040', '\164', '\150', '\040', '\061', '\012', '\162', '\112', + '\152', '\040', '\162', '\151', '\040', '\061', '\012', '\141', + '\130', '\167', '\040', '\141', '\156', '\040', '\061', '\012', + '\172', '\163', '\121', '\040', '\163', '\172', '\040', '\061', + '\012', '\143', '\121', '\155', '\040', '\143', '\150', '\040', + '\061', '\012', '\123', '\161', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\164', '\113', '\155', '\040', '\164', + '\150', '\040', '\061', '\012', '\150', '\166', '\117', '\040', + '\164', '\150', '\040', '\061', '\012', '\150', '\107', '\144', + '\040', '\164', '\150', '\040', '\061', '\012', '\127', '\142', + '\156', '\040', '\141', '\156', '\040', '\061', '\012', '\166', + '\103', '\146', '\040', '\166', '\141', '\040', '\061', '\012', + '\154', '\107', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\104', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\167', '\104', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\170', '\122', '\171', '\040', '\156', + '\171', '\040', '\061', '\012', '\166', '\130', '\151', '\040', + '\151', '\156', '\040', '\061', '\012', '\161', '\151', '\121', + '\040', '\161', '\165', '\040', '\061', '\012', '\143', '\106', + '\163', '\040', '\143', '\150', '\040', '\061', '\012', '\114', + '\150', '\160', '\040', '\164', '\150', '\040', '\061', '\012', + '\170', '\105', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\146', '\121', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\143', '\112', '\166', '\040', '\143', '\150', + '\040', '\061', '\012', '\154', '\172', '\117', '\040', '\154', + '\145', '\040', '\061', '\012', '\106', '\170', '\153', '\040', + '\153', '\141', '\040', '\061', '\012', '\164', '\104', '\144', + '\040', '\164', '\150', '\040', '\061', '\012', '\130', '\156', + '\170', '\040', '\141', '\156', '\040', '\061', '\012', '\164', + '\170', '\103', '\040', '\164', '\150', '\040', '\061', '\012', + '\164', '\107', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\166', '\107', '\040', '\163', '\172', '\040', + '\061', '\012', '\147', '\160', '\103', '\040', '\156', '\147', + '\040', '\061', '\012', '\160', '\170', '\104', '\040', '\160', + '\162', '\040', '\061', '\012', '\132', '\146', '\160', '\040', + '\160', '\162', '\040', '\061', '\012', '\157', '\127', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\166', '\166', + '\126', '\040', '\166', '\141', '\040', '\061', '\012', '\107', + '\167', '\146', '\040', '\167', '\141', '\040', '\061', '\012', + '\131', '\143', '\166', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\143', '\132', '\040', '\143', '\150', '\040', + '\061', '\012', '\155', '\115', '\167', '\040', '\155', '\145', + '\040', '\061', '\012', '\171', '\121', '\154', '\040', '\154', + '\145', '\040', '\061', '\012', '\165', '\107', '\160', '\040', + '\161', '\165', '\040', '\061', '\012', '\154', '\116', '\152', + '\040', '\154', '\145', '\040', '\061', '\012', '\131', '\143', + '\155', '\040', '\143', '\150', '\040', '\061', '\012', '\166', + '\111', '\170', '\040', '\166', '\141', '\040', '\061', '\012', + '\171', '\114', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\155', '\122', '\170', '\040', '\155', '\145', '\040', + '\061', '\012', '\156', '\162', '\113', '\040', '\141', '\156', + '\040', '\061', '\012', '\132', '\171', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\116', '\143', '\164', '\040', + '\164', '\150', '\040', '\061', '\012', '\121', '\155', '\154', + '\040', '\154', '\145', '\040', '\061', '\012', '\172', '\120', + '\144', '\040', '\144', '\145', '\040', '\061', '\012', '\144', + '\127', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\105', '\147', '\170', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\116', '\163', '\040', '\163', '\164', '\040', + '\061', '\012', '\163', '\116', '\154', '\040', '\154', '\145', + '\040', '\061', '\012', '\160', '\144', '\127', '\040', '\144', + '\145', '\040', '\061', '\012', '\123', '\156', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\171', '\162', '\120', + '\040', '\145', '\162', '\040', '\061', '\012', '\146', '\112', + '\154', '\040', '\154', '\145', '\040', '\061', '\012', '\164', + '\126', '\147', '\040', '\164', '\150', '\040', '\061', '\012', + '\152', '\166', '\103', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\150', '\116', '\040', '\164', '\150', '\040', + '\061', '\012', '\161', '\144', '\103', '\040', '\161', '\165', + '\040', '\061', '\012', '\160', '\155', '\124', '\040', '\155', + '\145', '\040', '\061', '\012', '\114', '\142', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\170', '\160', '\112', + '\040', '\160', '\162', '\040', '\061', '\012', '\155', '\131', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\142', + '\167', '\126', '\040', '\167', '\141', '\040', '\061', '\012', + '\167', '\152', '\104', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\161', '\103', '\040', '\161', '\165', '\040', + '\061', '\012', '\170', '\125', '\146', '\040', '\146', '\157', + '\040', '\061', '\012', '\144', '\150', '\125', '\040', '\164', + '\150', '\040', '\061', '\012', '\142', '\132', '\142', '\040', + '\142', '\145', '\040', '\061', '\012', '\164', '\167', '\104', + '\040', '\164', '\150', '\040', '\061', '\012', '\142', '\142', + '\115', '\040', '\142', '\145', '\040', '\061', '\012', '\150', + '\147', '\103', '\040', '\164', '\150', '\040', '\061', '\012', + '\144', '\113', '\142', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\112', '\155', '\040', '\166', '\141', '\040', + '\061', '\012', '\167', '\105', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\117', '\146', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\143', '\130', '\154', '\040', + '\143', '\150', '\040', '\061', '\012', '\167', '\160', '\126', + '\040', '\160', '\162', '\040', '\061', '\012', '\164', '\161', + '\115', '\040', '\164', '\150', '\040', '\061', '\012', '\160', + '\125', '\146', '\040', '\160', '\162', '\040', '\061', '\012', + '\124', '\167', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\115', '\147', '\161', '\040', '\156', '\147', '\040', + '\061', '\012', '\166', '\121', '\157', '\040', '\157', '\156', + '\040', '\061', '\012', '\171', '\152', '\124', '\040', '\151', + '\152', '\040', '\061', '\012', '\141', '\126', '\144', '\040', + '\141', '\156', '\040', '\061', '\012', '\145', '\110', '\160', + '\040', '\145', '\162', '\040', '\061', '\012', '\166', '\107', + '\166', '\040', '\166', '\141', '\040', '\061', '\012', '\163', + '\162', '\107', '\040', '\145', '\162', '\040', '\061', '\012', + '\161', '\126', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\154', '\115', '\040', '\164', '\150', '\040', + '\061', '\012', '\156', '\162', '\124', '\040', '\141', '\156', + '\040', '\061', '\012', '\172', '\122', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\143', '\114', '\162', '\040', + '\143', '\150', '\040', '\061', '\012', '\154', '\162', '\110', + '\040', '\145', '\162', '\040', '\061', '\012', '\167', '\124', + '\154', '\040', '\154', '\145', '\040', '\061', '\012', '\143', + '\166', '\111', '\040', '\143', '\150', '\040', '\061', '\012', + '\153', '\161', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\111', '\170', '\160', '\040', '\160', '\162', '\040', + '\061', '\012', '\170', '\145', '\121', '\040', '\145', '\162', + '\040', '\061', '\012', '\143', '\116', '\171', '\040', '\143', + '\150', '\040', '\061', '\012', '\153', '\122', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\162', '\165', '\131', + '\040', '\161', '\165', '\040', '\061', '\012', '\130', '\143', + '\161', '\040', '\143', '\150', '\040', '\061', '\012', '\113', + '\172', '\142', '\040', '\142', '\151', '\040', '\061', '\012', + '\127', '\170', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\152', '\115', '\040', '\151', '\152', '\040', + '\061', '\012', '\152', '\144', '\117', '\040', '\144', '\145', + '\040', '\061', '\012', '\112', '\146', '\171', '\040', '\156', + '\171', '\040', '\061', '\012', '\142', '\126', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\144', '\121', '\157', + '\040', '\157', '\156', '\040', '\061', '\012', '\156', '\143', + '\121', '\040', '\141', '\156', '\040', '\061', '\012', '\160', + '\126', '\167', '\040', '\160', '\162', '\040', '\061', '\012', + '\123', '\170', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\125', '\142', '\160', '\040', '\160', '\162', '\040', + '\061', '\012', '\167', '\166', '\103', '\040', '\166', '\141', + '\040', '\061', '\012', '\153', '\150', '\107', '\040', '\164', + '\150', '\040', '\061', '\012', '\143', '\161', '\106', '\040', + '\143', '\150', '\040', '\061', '\012', '\116', '\170', '\152', + '\040', '\151', '\152', '\040', '\061', '\012', '\167', '\104', + '\155', '\040', '\155', '\145', '\040', '\061', '\012', '\171', + '\104', '\144', '\040', '\144', '\145', '\040', '\061', '\012', + '\151', '\171', '\111', '\040', '\151', '\156', '\040', '\061', + '\012', '\145', '\130', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\150', '\161', '\120', '\040', '\164', '\150', + '\040', '\061', '\012', '\113', '\170', '\162', '\040', '\145', + '\162', '\040', '\061', '\012', '\166', '\163', '\131', '\040', + '\163', '\164', '\040', '\061', '\012', '\124', '\167', '\142', + '\040', '\167', '\141', '\040', '\061', '\012', '\146', '\161', + '\167', '\040', '\161', '\165', '\040', '\061', '\012', '\167', + '\155', '\103', '\040', '\155', '\145', '\040', '\061', '\012', + '\166', '\106', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\166', '\156', '\103', '\040', '\141', '\156', '\040', + '\061', '\012', '\156', '\127', '\161', '\040', '\141', '\156', + '\040', '\061', '\012', '\150', '\172', '\102', '\040', '\164', + '\150', '\040', '\061', '\012', '\113', '\146', '\153', '\040', + '\153', '\141', '\040', '\061', '\012', '\164', '\121', '\145', + '\040', '\164', '\150', '\040', '\061', '\012', '\152', '\165', + '\127', '\040', '\161', '\165', '\040', '\061', '\012', '\161', + '\154', '\130', '\040', '\161', '\165', '\040', '\061', '\012', + '\150', '\107', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\117', '\161', '\144', '\040', '\161', '\165', '\040', + '\061', '\012', '\116', '\160', '\167', '\040', '\160', '\162', + '\040', '\061', '\012', '\150', '\147', '\127', '\040', '\164', + '\150', '\040', '\061', '\012', '\146', '\170', '\115', '\040', + '\146', '\157', '\040', '\061', '\012', '\152', '\123', '\171', + '\040', '\151', '\152', '\040', '\061', '\012', '\146', '\112', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\155', + '\152', '\107', '\040', '\151', '\152', '\040', '\061', '\012', + '\164', '\147', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\117', '\147', '\170', '\040', '\156', '\147', '\040', + '\061', '\012', '\110', '\142', '\170', '\040', '\142', '\145', + '\040', '\061', '\012', '\114', '\152', '\154', '\040', '\154', + '\145', '\040', '\061', '\012', '\151', '\166', '\132', '\040', + '\151', '\156', '\040', '\061', '\012', '\142', '\155', '\131', + '\040', '\155', '\145', '\040', '\061', '\012', '\121', '\146', + '\160', '\040', '\160', '\162', '\040', '\061', '\012', '\167', + '\146', '\121', '\040', '\167', '\141', '\040', '\061', '\012', + '\150', '\103', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\165', '\125', '\040', '\161', '\165', '\040', + '\061', '\012', '\171', '\144', '\132', '\040', '\144', '\145', + '\040', '\061', '\012', '\166', '\126', '\153', '\040', '\153', + '\141', '\040', '\061', '\012', '\155', '\132', '\146', '\040', + '\155', '\145', '\040', '\061', '\012', '\154', '\117', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\161', '\111', + '\166', '\040', '\161', '\165', '\040', '\061', '\012', '\170', + '\132', '\142', '\040', '\142', '\145', '\040', '\061', '\012', + '\170', '\161', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\155', '\171', '\040', '\155', '\145', '\040', + '\061', '\012', '\112', '\161', '\151', '\040', '\161', '\165', + '\040', '\061', '\012', '\143', '\170', '\114', '\040', '\143', + '\150', '\040', '\061', '\012', '\132', '\164', '\161', '\040', + '\164', '\150', '\040', '\061', '\012', '\164', '\144', '\124', + '\040', '\164', '\150', '\040', '\061', '\012', '\165', '\127', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\170', + '\107', '\172', '\040', '\163', '\172', '\040', '\061', '\012', + '\127', '\167', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\160', '\102', '\153', '\040', '\153', '\141', '\040', + '\061', '\012', '\171', '\161', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\143', '\131', '\154', '\040', '\143', + '\150', '\040', '\061', '\012', '\171', '\156', '\127', '\040', + '\141', '\156', '\040', '\061', '\012', '\167', '\171', '\112', + '\040', '\167', '\141', '\040', '\061', '\012', '\161', '\107', + '\171', '\040', '\161', '\165', '\040', '\061', '\012', '\146', + '\116', '\160', '\040', '\160', '\162', '\040', '\061', '\012', + '\150', '\106', '\163', '\040', '\164', '\150', '\040', '\061', + '\012', '\131', '\170', '\165', '\040', '\161', '\165', '\040', + '\061', '\012', '\153', '\166', '\112', '\040', '\153', '\141', + '\040', '\061', '\012', '\106', '\170', '\172', '\040', '\163', + '\172', '\040', '\061', '\012', '\164', '\167', '\107', '\040', + '\164', '\150', '\040', '\061', '\012', '\161', '\166', '\107', + '\040', '\161', '\165', '\040', '\061', '\012', '\166', '\122', + '\160', '\040', '\166', '\141', '\040', '\061', '\012', '\121', + '\161', '\151', '\040', '\161', '\165', '\040', '\061', '\012', + '\147', '\172', '\105', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\116', '\154', '\040', '\154', '\145', '\040', + '\061', '\012', '\172', '\160', '\127', '\040', '\163', '\172', + '\040', '\061', '\012', '\144', '\143', '\120', '\040', '\143', + '\150', '\040', '\061', '\012', '\143', '\120', '\170', '\040', + '\143', '\150', '\040', '\061', '\012', '\167', '\143', '\121', + '\040', '\143', '\150', '\040', '\061', '\012', '\160', '\121', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\161', + '\171', '\106', '\040', '\161', '\165', '\040', '\061', '\012', + '\172', '\143', '\130', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\161', '\153', '\040', '\161', '\165', '\040', + '\061', '\012', '\153', '\155', '\131', '\040', '\153', '\141', + '\040', '\061', '\012', '\161', '\154', '\107', '\040', '\161', + '\165', '\040', '\061', '\012', '\170', '\105', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\160', '\161', '\126', + '\040', '\161', '\165', '\040', '\061', '\012', '\117', '\150', + '\160', '\040', '\164', '\150', '\040', '\061', '\012', '\170', + '\144', '\115', '\040', '\144', '\145', '\040', '\061', '\012', + '\146', '\114', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\161', '\101', '\145', '\040', '\161', '\165', '\040', + '\061', '\012', '\130', '\167', '\166', '\040', '\166', '\141', + '\040', '\061', '\012', '\114', '\172', '\151', '\040', '\151', + '\156', '\040', '\061', '\012', '\161', '\117', '\153', '\040', + '\161', '\165', '\040', '\061', '\012', '\143', '\130', '\156', + '\040', '\141', '\156', '\040', '\061', '\012', '\113', '\144', + '\163', '\040', '\144', '\145', '\040', '\061', '\012', '\147', + '\166', '\125', '\040', '\156', '\147', '\040', '\061', '\012', + '\146', '\120', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\156', '\132', '\162', '\040', '\141', '\156', '\040', + '\061', '\012', '\110', '\170', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\146', '\103', '\155', '\040', '\155', + '\145', '\040', '\061', '\012', '\161', '\146', '\104', '\040', + '\161', '\165', '\040', '\061', '\012', '\127', '\146', '\166', + '\040', '\166', '\141', '\040', '\061', '\012', '\161', '\146', + '\142', '\040', '\161', '\165', '\040', '\061', '\012', '\152', + '\161', '\103', '\040', '\161', '\165', '\040', '\061', '\012', + '\146', '\165', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\146', '\101', '\040', '\161', '\165', '\040', + '\061', '\012', '\122', '\154', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\170', '\152', '\104', '\040', '\151', + '\152', '\040', '\061', '\012', '\167', '\164', '\106', '\040', + '\164', '\150', '\040', '\061', '\012', '\130', '\155', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\160', '\127', + '\160', '\040', '\160', '\162', '\040', '\061', '\012', '\121', + '\170', '\166', '\040', '\166', '\141', '\040', '\061', '\012', + '\172', '\126', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\155', '\132', '\040', '\156', '\147', '\040', + '\061', '\012', '\161', '\144', '\125', '\040', '\161', '\165', + '\040', '\061', '\012', '\152', '\161', '\126', '\040', '\161', + '\165', '\040', '\061', '\012', '\147', '\130', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\161', '\155', '\113', + '\040', '\161', '\165', '\040', '\061', '\012', '\107', '\146', + '\152', '\040', '\151', '\152', '\040', '\061', '\012', '\143', + '\121', '\162', '\040', '\143', '\150', '\040', '\061', '\012', + '\131', '\150', '\162', '\040', '\164', '\150', '\040', '\061', + '\012', '\166', '\166', '\123', '\040', '\166', '\141', '\040', + '\061', '\012', '\165', '\104', '\142', '\040', '\161', '\165', + '\040', '\061', '\012', '\143', '\144', '\102', '\040', '\143', + '\150', '\040', '\061', '\012', '\142', '\166', '\105', '\040', + '\166', '\141', '\040', '\061', '\012', '\170', '\166', '\123', + '\040', '\166', '\141', '\040', '\061', '\012', '\152', '\122', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\162', + '\166', '\104', '\040', '\145', '\162', '\040', '\061', '\012', + '\130', '\171', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\112', '\146', '\151', '\040', '\151', '\156', '\040', + '\061', '\012', '\141', '\102', '\167', '\040', '\141', '\156', + '\040', '\061', '\012', '\156', '\127', '\143', '\040', '\141', + '\156', '\040', '\061', '\012', '\170', '\102', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\153', '\147', '\131', + '\040', '\156', '\147', '\040', '\061', '\012', '\142', '\107', + '\142', '\040', '\142', '\151', '\040', '\061', '\012', '\147', + '\152', '\105', '\040', '\156', '\147', '\040', '\061', '\012', + '\122', '\154', '\167', '\040', '\154', '\145', '\040', '\061', + '\012', '\167', '\162', '\124', '\040', '\145', '\162', '\040', + '\061', '\012', '\142', '\121', '\162', '\040', '\145', '\162', + '\040', '\061', '\012', '\154', '\152', '\131', '\040', '\154', + '\145', '\040', '\061', '\012', '\161', '\166', '\125', '\040', + '\161', '\165', '\040', '\061', '\012', '\146', '\113', '\155', + '\040', '\155', '\145', '\040', '\061', '\012', '\160', '\124', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\172', + '\124', '\167', '\040', '\163', '\172', '\040', '\061', '\012', + '\161', '\156', '\126', '\040', '\141', '\156', '\040', '\061', + '\012', '\162', '\127', '\170', '\040', '\145', '\162', '\040', + '\061', '\012', '\156', '\127', '\144', '\040', '\141', '\156', + '\040', '\061', '\012', '\156', '\113', '\146', '\040', '\141', + '\156', '\040', '\061', '\012', '\153', '\115', '\146', '\040', + '\153', '\141', '\040', '\061', '\012', '\146', '\153', '\107', + '\040', '\153', '\141', '\040', '\061', '\012', '\142', '\167', + '\130', '\040', '\167', '\141', '\040', '\061', '\012', '\143', + '\167', '\126', '\040', '\143', '\150', '\040', '\061', '\012', + '\165', '\167', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\114', '\166', '\040', '\145', '\162', '\040', + '\061', '\012', '\172', '\115', '\142', '\040', '\163', '\172', + '\040', '\061', '\012', '\172', '\160', '\132', '\040', '\163', + '\172', '\040', '\061', '\012', '\162', '\115', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\124', '\164', '\152', + '\040', '\164', '\150', '\040', '\061', '\012', '\147', '\166', + '\117', '\040', '\156', '\147', '\040', '\061', '\012', '\112', + '\143', '\172', '\040', '\143', '\150', '\040', '\061', '\012', + '\103', '\171', '\170', '\040', '\156', '\171', '\040', '\061', + '\012', '\156', '\152', '\130', '\040', '\141', '\156', '\040', + '\061', '\012', '\141', '\126', '\170', '\040', '\141', '\156', + '\040', '\061', '\012', '\161', '\130', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\125', '\161', '\163', '\040', + '\161', '\165', '\040', '\061', '\012', '\144', '\126', '\172', + '\040', '\144', '\145', '\040', '\061', '\012', '\122', '\143', + '\160', '\040', '\143', '\150', '\040', '\061', '\012', '\145', + '\113', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\130', '\172', '\156', '\040', '\151', '\156', '\040', '\061', + '\012', '\166', '\171', '\106', '\040', '\166', '\141', '\040', + '\061', '\012', '\113', '\154', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\170', '\144', '\111', '\040', '\144', + '\145', '\040', '\061', '\012', '\110', '\161', '\142', '\040', + '\161', '\165', '\040', '\061', '\012', '\170', '\105', '\145', + '\040', '\145', '\162', '\040', '\061', '\012', '\161', '\160', + '\111', '\040', '\161', '\165', '\040', '\061', '\012', '\147', + '\104', '\170', '\040', '\156', '\147', '\040', '\061', '\012', + '\112', '\150', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\165', '\113', '\040', '\165', '\156', '\040', + '\061', '\012', '\166', '\147', '\125', '\040', '\156', '\147', + '\040', '\061', '\012', '\162', '\127', '\166', '\040', '\145', + '\162', '\040', '\061', '\012', '\120', '\156', '\155', '\040', + '\141', '\156', '\040', '\061', '\012', '\156', '\114', '\155', + '\040', '\141', '\156', '\040', '\061', '\012', '\102', '\150', + '\152', '\040', '\164', '\150', '\040', '\061', '\012', '\142', + '\120', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\152', '\160', '\111', '\040', '\151', '\152', '\040', '\061', + '\012', '\164', '\114', '\172', '\040', '\164', '\150', '\040', + '\061', '\012', '\166', '\160', '\123', '\040', '\166', '\141', + '\040', '\061', '\012', '\106', '\170', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\161', '\104', '\163', '\040', + '\161', '\165', '\040', '\061', '\012', '\167', '\172', '\115', + '\040', '\163', '\172', '\040', '\061', '\012', '\147', '\167', + '\112', '\040', '\156', '\147', '\040', '\061', '\012', '\172', + '\102', '\167', '\040', '\163', '\172', '\040', '\061', '\012', + '\161', '\107', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\114', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\102', '\152', '\154', '\040', '\154', '\145', + '\040', '\061', '\012', '\150', '\146', '\110', '\040', '\164', + '\150', '\040', '\061', '\012', '\143', '\154', '\127', '\040', + '\143', '\150', '\040', '\061', '\012', '\122', '\147', '\153', + '\040', '\156', '\147', '\040', '\061', '\012', '\107', '\163', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\125', + '\166', '\170', '\040', '\166', '\141', '\040', '\061', '\012', + '\121', '\147', '\166', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\146', '\130', '\040', '\156', '\147', '\040', + '\061', '\012', '\162', '\121', '\166', '\040', '\145', '\162', + '\040', '\061', '\012', '\170', '\166', '\107', '\040', '\166', + '\141', '\040', '\061', '\012', '\153', '\152', '\170', '\040', + '\151', '\152', '\040', '\061', '\012', '\144', '\107', '\146', + '\040', '\144', '\145', '\040', '\061', '\012', '\146', '\143', + '\101', '\040', '\143', '\150', '\040', '\061', '\012', '\105', + '\150', '\161', '\040', '\164', '\150', '\040', '\061', '\012', + '\172', '\102', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\107', '\160', '\153', '\040', '\153', '\141', '\040', + '\061', '\012', '\164', '\102', '\166', '\040', '\164', '\150', + '\040', '\061', '\012', '\130', '\146', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\171', '\112', '\155', '\040', + '\155', '\145', '\040', '\061', '\012', '\163', '\161', '\124', + '\040', '\161', '\165', '\040', '\061', '\012', '\160', '\162', + '\131', '\040', '\145', '\162', '\040', '\061', '\012', '\104', + '\161', '\157', '\040', '\161', '\165', '\040', '\061', '\012', + '\112', '\172', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\115', '\160', '\040', '\161', '\165', '\040', + '\061', '\012', '\171', '\146', '\115', '\040', '\156', '\171', + '\040', '\061', '\012', '\107', '\170', '\146', '\040', '\146', + '\157', '\040', '\061', '\012', '\167', '\172', '\120', '\040', + '\163', '\172', '\040', '\061', '\012', '\172', '\116', '\155', + '\040', '\163', '\172', '\040', '\061', '\012', '\167', '\113', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\122', + '\162', '\144', '\040', '\145', '\162', '\040', '\061', '\012', + '\110', '\166', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\147', '\146', '\104', '\040', '\156', '\147', '\040', + '\061', '\012', '\127', '\155', '\172', '\040', '\163', '\172', + '\040', '\061', '\012', '\143', '\112', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\156', '\124', '\146', '\040', + '\141', '\156', '\040', '\061', '\012', '\165', '\166', '\127', + '\040', '\161', '\165', '\040', '\061', '\012', '\165', '\120', + '\146', '\040', '\161', '\165', '\040', '\061', '\012', '\166', + '\167', '\122', '\040', '\166', '\141', '\040', '\061', '\012', + '\142', '\115', '\146', '\040', '\142', '\145', '\040', '\061', + '\012', '\167', '\111', '\165', '\040', '\161', '\165', '\040', + '\061', '\012', '\153', '\170', '\131', '\040', '\153', '\141', + '\040', '\061', '\012', '\147', '\132', '\153', '\040', '\156', + '\147', '\040', '\061', '\012', '\161', '\106', '\144', '\040', + '\161', '\165', '\040', '\061', '\012', '\142', '\115', '\154', + '\040', '\154', '\145', '\040', '\061', '\012', '\167', '\110', + '\154', '\040', '\154', '\145', '\040', '\061', '\012', '\167', + '\126', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\167', '\154', '\130', '\040', '\154', '\145', '\040', '\061', + '\012', '\146', '\163', '\114', '\040', '\163', '\164', '\040', + '\061', '\012', '\160', '\122', '\146', '\040', '\160', '\162', + '\040', '\061', '\012', '\172', '\163', '\130', '\040', '\163', + '\164', '\040', '\061', '\012', '\161', '\102', '\153', '\040', + '\161', '\165', '\040', '\061', '\012', '\130', '\172', '\160', + '\040', '\163', '\172', '\040', '\061', '\012', '\152', '\144', + '\122', '\040', '\144', '\145', '\040', '\061', '\012', '\132', + '\154', '\172', '\040', '\154', '\145', '\040', '\061', '\012', + '\127', '\146', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\122', '\152', '\166', '\040', '\151', '\152', '\040', + '\061', '\012', '\166', '\106', '\172', '\040', '\163', '\172', + '\040', '\061', '\012', '\164', '\153', '\126', '\040', '\164', + '\150', '\040', '\061', '\012', '\130', '\142', '\167', '\040', + '\167', '\141', '\040', '\061', '\012', '\170', '\121', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\113', '\170', + '\171', '\040', '\156', '\171', '\040', '\061', '\012', '\170', + '\103', '\166', '\040', '\166', '\141', '\040', '\061', '\012', + '\156', '\161', '\126', '\040', '\141', '\156', '\040', '\061', + '\012', '\127', '\167', '\170', '\040', '\167', '\141', '\040', + '\061', '\012', '\153', '\144', '\127', '\040', '\144', '\145', + '\040', '\061', '\012', '\160', '\153', '\111', '\040', '\153', + '\141', '\040', '\061', '\012', '\157', '\150', '\123', '\040', + '\164', '\150', '\040', '\061', '\012', '\132', '\144', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\155', '\103', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\163', + '\170', '\114', '\040', '\163', '\164', '\040', '\061', '\012', + '\121', '\162', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\130', '\167', '\040', '\161', '\165', '\040', + '\061', '\012', '\167', '\161', '\121', '\040', '\161', '\165', + '\040', '\061', '\012', '\151', '\152', '\113', '\040', '\151', + '\156', '\040', '\061', '\012', '\163', '\106', '\172', '\040', + '\163', '\164', '\040', '\061', '\012', '\110', '\154', '\167', + '\040', '\154', '\145', '\040', '\061', '\012', '\107', '\161', + '\156', '\040', '\141', '\156', '\040', '\061', '\012', '\170', + '\120', '\153', '\040', '\153', '\141', '\040', '\061', '\012', + '\167', '\132', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\161', '\155', '\040', '\161', '\165', '\040', + '\061', '\012', '\114', '\172', '\160', '\040', '\163', '\172', + '\040', '\061', '\012', '\102', '\144', '\172', '\040', '\144', + '\145', '\040', '\061', '\012', '\167', '\121', '\154', '\040', + '\154', '\145', '\040', '\061', '\012', '\167', '\164', '\112', + '\040', '\164', '\150', '\040', '\061', '\012', '\125', '\171', + '\151', '\040', '\151', '\156', '\040', '\061', '\012', '\127', + '\143', '\171', '\040', '\143', '\150', '\040', '\061', '\012', + '\167', '\161', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\102', '\156', '\163', '\040', '\141', '\156', '\040', + '\061', '\012', '\143', '\104', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\170', '\112', '\166', '\040', '\166', + '\141', '\040', '\061', '\012', '\127', '\146', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\170', '\150', '\120', + '\040', '\164', '\150', '\040', '\061', '\012', '\143', '\127', + '\160', '\040', '\143', '\150', '\040', '\061', '\012', '\162', + '\161', '\132', '\040', '\161', '\165', '\040', '\061', '\012', + '\142', '\153', '\102', '\040', '\153', '\141', '\040', '\061', + '\012', '\127', '\164', '\154', '\040', '\164', '\150', '\040', + '\061', '\012', '\147', '\172', '\146', '\040', '\156', '\147', + '\040', '\061', '\012', '\142', '\115', '\162', '\040', '\145', + '\162', '\040', '\061', '\012', '\160', '\170', '\116', '\040', + '\160', '\162', '\040', '\061', '\012', '\166', '\150', '\126', + '\040', '\164', '\150', '\040', '\061', '\012', '\153', '\161', + '\130', '\040', '\161', '\165', '\040', '\061', '\012', '\113', + '\144', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\166', '\121', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\171', '\153', '\103', '\040', '\153', '\141', '\040', + '\061', '\012', '\172', '\115', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\105', '\161', '\172', '\040', '\161', + '\165', '\040', '\061', '\012', '\154', '\130', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\172', '\155', '\132', + '\040', '\163', '\172', '\040', '\061', '\012', '\161', '\160', + '\102', '\040', '\161', '\165', '\040', '\061', '\012', '\166', + '\107', '\152', '\040', '\151', '\152', '\040', '\061', '\012', + '\124', '\152', '\170', '\040', '\172', '\152', '\040', '\061', + '\012', '\164', '\166', '\113', '\040', '\164', '\150', '\040', + '\061', '\012', '\147', '\131', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\154', '\106', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\151', '\112', '\164', '\040', + '\164', '\150', '\040', '\061', '\012', '\120', '\153', '\170', + '\040', '\153', '\141', '\040', '\061', '\012', '\143', '\104', + '\166', '\040', '\143', '\150', '\040', '\061', '\012', '\131', + '\171', '\144', '\040', '\144', '\145', '\040', '\061', '\012', + '\126', '\143', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\130', '\150', '\161', '\040', '\164', '\150', '\040', + '\061', '\012', '\172', '\116', '\146', '\040', '\163', '\172', + '\040', '\061', '\012', '\166', '\143', '\104', '\040', '\143', + '\150', '\040', '\061', '\012', '\142', '\156', '\127', '\040', + '\141', '\156', '\040', '\061', '\012', '\165', '\166', '\121', + '\040', '\161', '\165', '\040', '\061', '\012', '\132', '\172', + '\152', '\040', '\163', '\172', '\040', '\061', '\012', '\147', + '\120', '\152', '\040', '\156', '\147', '\040', '\061', '\012', + '\152', '\167', '\104', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\160', '\117', '\040', '\151', '\152', '\040', + '\061', '\012', '\142', '\104', '\170', '\040', '\142', '\145', + '\040', '\061', '\012', '\166', '\105', '\151', '\040', '\151', + '\156', '\040', '\061', '\012', '\132', '\143', '\164', '\040', + '\164', '\150', '\040', '\061', '\012', '\167', '\162', '\130', + '\040', '\145', '\162', '\040', '\061', '\012', '\144', '\150', + '\123', '\040', '\164', '\150', '\040', '\061', '\012', '\172', + '\152', '\112', '\040', '\163', '\172', '\040', '\061', '\012', + '\144', '\104', '\153', '\040', '\144', '\145', '\040', '\061', + '\012', '\163', '\162', '\112', '\040', '\145', '\162', '\040', + '\061', '\012', '\141', '\127', '\147', '\040', '\141', '\156', + '\040', '\061', '\012', '\155', '\166', '\112', '\040', '\166', + '\141', '\040', '\061', '\012', '\131', '\164', '\143', '\040', + '\164', '\150', '\040', '\061', '\012', '\152', '\151', '\121', + '\040', '\151', '\156', '\040', '\061', '\012', '\164', '\106', + '\172', '\040', '\164', '\150', '\040', '\061', '\012', '\163', + '\112', '\154', '\040', '\154', '\145', '\040', '\061', '\012', + '\166', '\132', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\125', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\157', '\161', '\102', '\040', '\161', '\165', + '\040', '\061', '\012', '\170', '\104', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\150', '\146', '\105', '\040', + '\164', '\150', '\040', '\061', '\012', '\155', '\123', '\142', + '\040', '\155', '\145', '\040', '\061', '\012', '\152', '\155', + '\122', '\040', '\151', '\152', '\040', '\061', '\012', '\162', + '\106', '\160', '\040', '\145', '\162', '\040', '\061', '\012', + '\130', '\152', '\171', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\120', '\160', '\040', '\160', '\162', '\040', + '\061', '\012', '\151', '\161', '\121', '\040', '\164', '\151', + '\040', '\061', '\012', '\155', '\146', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\164', '\170', '\114', '\040', + '\164', '\150', '\040', '\061', '\012', '\152', '\102', '\144', + '\040', '\144', '\145', '\040', '\061', '\012', '\130', '\166', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\144', + '\166', '\131', '\040', '\144', '\145', '\040', '\061', '\012', + '\163', '\144', '\115', '\040', '\144', '\145', '\040', '\061', + '\012', '\170', '\147', '\131', '\040', '\156', '\147', '\040', + '\061', '\012', '\162', '\131', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\166', '\154', '\101', '\040', '\154', + '\145', '\040', '\061', '\012', '\160', '\106', '\142', '\040', + '\160', '\162', '\040', '\061', '\012', '\171', '\106', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\147', '\143', + '\113', '\040', '\143', '\150', '\040', '\061', '\012', '\170', + '\146', '\132', '\040', '\146', '\157', '\040', '\061', '\012', + '\152', '\104', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\116', '\166', '\040', '\166', '\141', '\040', + '\061', '\012', '\164', '\113', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\167', '\164', '\125', '\040', '\164', + '\150', '\040', '\061', '\012', '\142', '\110', '\153', '\040', + '\153', '\141', '\040', '\061', '\012', '\161', '\103', '\167', + '\040', '\161', '\165', '\040', '\061', '\012', '\132', '\143', + '\141', '\040', '\141', '\156', '\040', '\061', '\012', '\153', + '\104', '\167', '\040', '\153', '\141', '\040', '\061', '\012', + '\131', '\167', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\130', '\163', '\040', '\163', '\164', '\040', + '\061', '\012', '\171', '\115', '\155', '\040', '\155', '\145', + '\040', '\061', '\012', '\107', '\167', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\155', '\131', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\167', '\103', '\170', + '\040', '\167', '\141', '\040', '\061', '\012', '\152', '\132', + '\170', '\040', '\151', '\152', '\040', '\061', '\012', '\157', + '\121', '\144', '\040', '\157', '\156', '\040', '\061', '\012', + '\106', '\172', '\153', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\167', '\106', '\040', '\154', '\145', '\040', + '\061', '\012', '\130', '\172', '\153', '\040', '\163', '\172', + '\040', '\061', '\012', '\116', '\152', '\170', '\040', '\151', + '\152', '\040', '\061', '\012', '\171', '\157', '\111', '\040', + '\157', '\156', '\040', '\061', '\012', '\163', '\112', '\155', + '\040', '\163', '\164', '\040', '\061', '\012', '\167', '\113', + '\153', '\040', '\153', '\141', '\040', '\061', '\012', '\121', + '\164', '\150', '\040', '\143', '\150', '\040', '\061', '\012', + '\114', '\154', '\172', '\040', '\154', '\145', '\040', '\061', + '\012', '\147', '\126', '\146', '\040', '\147', '\151', '\040', + '\061', '\012', '\160', '\120', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\154', '\107', '\171', '\040', '\154', + '\145', '\040', '\061', '\012', '\147', '\172', '\122', '\040', + '\156', '\147', '\040', '\061', '\012', '\162', '\130', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\116', '\160', + '\146', '\040', '\160', '\162', '\040', '\061', '\012', '\167', + '\166', '\122', '\040', '\166', '\141', '\040', '\061', '\012', + '\171', '\130', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\155', '\115', '\154', '\040', '\154', '\151', '\040', + '\061', '\012', '\142', '\131', '\170', '\040', '\142', '\145', + '\040', '\061', '\012', '\146', '\172', '\132', '\040', '\163', + '\172', '\040', '\061', '\012', '\166', '\162', '\107', '\040', + '\145', '\162', '\040', '\061', '\012', '\113', '\144', '\153', + '\040', '\144', '\145', '\040', '\061', '\012', '\171', '\161', + '\167', '\040', '\161', '\165', '\040', '\061', '\012', '\114', + '\153', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\152', '\113', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\132', '\161', '\170', '\040', '\161', '\165', '\040', + '\061', '\012', '\120', '\146', '\155', '\040', '\155', '\145', + '\040', '\061', '\012', '\162', '\154', '\127', '\040', '\145', + '\162', '\040', '\061', '\012', '\150', '\120', '\166', '\040', + '\164', '\150', '\040', '\061', '\012', '\117', '\152', '\170', + '\040', '\151', '\152', '\040', '\061', '\012', '\107', '\164', + '\161', '\040', '\164', '\150', '\040', '\061', '\012', '\166', + '\164', '\112', '\040', '\164', '\150', '\040', '\061', '\012', + '\127', '\154', '\171', '\040', '\154', '\145', '\040', '\061', + '\012', '\171', '\110', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\153', '\121', '\142', '\040', '\153', '\141', + '\040', '\061', '\012', '\114', '\144', '\143', '\040', '\144', + '\145', '\040', '\061', '\012', '\163', '\125', '\170', '\040', + '\163', '\164', '\040', '\061', '\012', '\143', '\112', '\147', + '\040', '\143', '\150', '\040', '\061', '\012', '\146', '\114', + '\144', '\040', '\144', '\145', '\040', '\061', '\012', '\115', + '\152', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\103', '\152', '\155', '\040', '\151', '\152', '\040', '\061', + '\012', '\141', '\167', '\130', '\040', '\141', '\156', '\040', + '\061', '\012', '\107', '\164', '\154', '\040', '\164', '\150', + '\040', '\061', '\012', '\167', '\172', '\116', '\040', '\163', + '\172', '\040', '\061', '\012', '\142', '\161', '\170', '\040', + '\161', '\165', '\040', '\061', '\012', '\146', '\101', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\145', '\172', + '\130', '\040', '\145', '\162', '\040', '\061', '\012', '\143', + '\102', '\170', '\040', '\143', '\150', '\040', '\061', '\012', + '\143', '\163', '\130', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\125', '\146', '\040', '\143', '\150', '\040', + '\061', '\012', '\161', '\163', '\112', '\040', '\161', '\165', + '\040', '\061', '\012', '\150', '\163', '\132', '\040', '\164', + '\150', '\040', '\061', '\012', '\161', '\172', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\121', '\147', '\153', + '\040', '\156', '\147', '\040', '\061', '\012', '\116', '\170', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\110', + '\161', '\141', '\040', '\141', '\156', '\040', '\061', '\012', + '\162', '\130', '\154', '\040', '\145', '\162', '\040', '\061', + '\012', '\156', '\154', '\120', '\040', '\141', '\156', '\040', + '\061', '\012', '\141', '\126', '\147', '\040', '\141', '\156', + '\040', '\061', '\012', '\171', '\150', '\107', '\040', '\164', + '\150', '\040', '\061', '\012', '\153', '\146', '\101', '\040', + '\153', '\141', '\040', '\061', '\012', '\126', '\155', '\153', + '\040', '\155', '\107', '\040', '\061', '\012', '\152', '\113', + '\155', '\040', '\151', '\152', '\040', '\061', '\012', '\150', + '\120', '\144', '\040', '\164', '\150', '\040', '\061', '\012', + '\141', '\120', '\144', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\131', '\171', '\040', '\142', '\145', '\040', + '\061', '\012', '\142', '\156', '\132', '\040', '\141', '\156', + '\040', '\061', '\012', '\107', '\163', '\152', '\040', '\163', + '\164', '\040', '\061', '\012', '\153', '\170', '\121', '\040', + '\153', '\141', '\040', '\061', '\012', '\166', '\153', '\106', + '\040', '\153', '\141', '\040', '\061', '\012', '\152', '\172', + '\123', '\040', '\163', '\172', '\040', '\061', '\012', '\146', + '\127', '\155', '\040', '\155', '\145', '\040', '\061', '\012', + '\121', '\143', '\165', '\040', '\143', '\150', '\040', '\061', + '\012', '\162', '\132', '\146', '\040', '\145', '\162', '\040', + '\061', '\012', '\152', '\142', '\132', '\040', '\151', '\152', + '\040', '\061', '\012', '\141', '\121', '\152', '\040', '\141', + '\156', '\040', '\061', '\012', '\142', '\172', '\117', '\040', + '\163', '\172', '\040', '\061', '\012', '\146', '\132', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\154', '\162', + '\116', '\040', '\145', '\162', '\040', '\061', '\012', '\146', + '\153', '\114', '\040', '\153', '\141', '\040', '\061', '\012', + '\104', '\161', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\153', '\103', '\040', '\163', '\172', '\040', + '\061', '\012', '\163', '\114', '\167', '\040', '\163', '\164', + '\040', '\061', '\012', '\116', '\166', '\162', '\040', '\145', + '\162', '\040', '\061', '\012', '\116', '\142', '\171', '\040', + '\142', '\145', '\040', '\061', '\012', '\145', '\115', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\167', '\106', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\103', + '\170', '\172', '\040', '\163', '\172', '\040', '\061', '\012', + '\151', '\132', '\160', '\040', '\151', '\156', '\040', '\061', + '\012', '\144', '\166', '\132', '\040', '\144', '\145', '\040', + '\061', '\012', '\166', '\111', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\161', '\103', '\154', '\040', '\161', + '\165', '\040', '\061', '\012', '\120', '\172', '\157', '\040', + '\157', '\156', '\040', '\061', '\012', '\166', '\116', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\172', '\161', + '\113', '\040', '\161', '\165', '\040', '\061', '\012', '\114', + '\155', '\170', '\040', '\155', '\145', '\040', '\061', '\012', + '\170', '\126', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\154', '\104', '\040', '\156', '\147', '\040', + '\061', '\012', '\107', '\142', '\146', '\040', '\142', '\145', + '\040', '\061', '\012', '\112', '\166', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\172', '\106', '\167', '\040', + '\163', '\172', '\040', '\061', '\012', '\164', '\115', '\161', + '\040', '\164', '\150', '\040', '\061', '\012', '\166', '\153', + '\112', '\040', '\153', '\141', '\040', '\061', '\012', '\123', + '\170', '\165', '\040', '\161', '\165', '\040', '\061', '\012', + '\141', '\146', '\125', '\040', '\141', '\156', '\040', '\061', + '\012', '\155', '\110', '\142', '\040', '\155', '\145', '\040', + '\061', '\012', '\152', '\170', '\125', '\040', '\151', '\152', + '\040', '\061', '\012', '\143', '\112', '\154', '\040', '\143', + '\150', '\040', '\061', '\012', '\165', '\161', '\105', '\040', + '\161', '\165', '\040', '\061', '\012', '\116', '\161', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\170', '\107', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\143', + '\172', '\107', '\040', '\143', '\150', '\040', '\061', '\012', + '\113', '\146', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\127', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\171', '\130', '\155', '\040', '\155', '\145', + '\040', '\061', '\012', '\146', '\156', '\104', '\040', '\141', + '\156', '\040', '\061', '\012', '\112', '\162', '\144', '\040', + '\145', '\162', '\040', '\061', '\012', '\157', '\170', '\132', + '\040', '\157', '\156', '\040', '\061', '\012', '\150', '\130', + '\156', '\040', '\164', '\150', '\040', '\061', '\012', '\146', + '\161', '\111', '\040', '\161', '\165', '\040', '\061', '\012', + '\167', '\101', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\151', '\107', '\153', '\040', '\151', '\156', '\040', + '\061', '\012', '\170', '\105', '\167', '\040', '\167', '\141', + '\040', '\061', '\012', '\146', '\126', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\171', '\164', '\125', '\040', + '\164', '\150', '\040', '\061', '\012', '\142', '\150', '\107', + '\040', '\164', '\150', '\040', '\061', '\012', '\157', '\121', + '\172', '\040', '\157', '\156', '\040', '\061', '\012', '\160', + '\147', '\117', '\040', '\156', '\147', '\040', '\061', '\012', + '\131', '\161', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\112', '\151', '\040', '\151', '\156', '\040', + '\061', '\012', '\153', '\143', '\126', '\040', '\143', '\150', + '\040', '\061', '\012', '\153', '\156', '\115', '\040', '\141', + '\156', '\040', '\061', '\012', '\103', '\167', '\162', '\040', + '\145', '\162', '\040', '\061', '\012', '\127', '\147', '\144', + '\040', '\156', '\147', '\040', '\061', '\012', '\142', '\160', + '\124', '\040', '\160', '\162', '\040', '\061', '\012', '\112', + '\144', '\152', '\040', '\144', '\145', '\040', '\061', '\012', + '\116', '\142', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\167', '\112', '\040', '\164', '\150', '\040', + '\061', '\012', '\121', '\145', '\160', '\040', '\145', '\162', + '\040', '\061', '\012', '\113', '\144', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\153', '\121', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\162', '\120', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\154', '\127', + '\160', '\040', '\154', '\145', '\040', '\061', '\012', '\106', + '\142', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\142', '\126', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\172', '\154', '\111', '\040', '\154', '\145', '\040', + '\061', '\012', '\102', '\172', '\160', '\040', '\163', '\172', + '\040', '\061', '\012', '\152', '\146', '\113', '\040', '\151', + '\152', '\040', '\061', '\012', '\131', '\166', '\155', '\040', + '\166', '\141', '\040', '\061', '\012', '\106', '\164', '\155', + '\040', '\164', '\150', '\040', '\061', '\012', '\141', '\115', + '\152', '\040', '\141', '\156', '\040', '\061', '\012', '\172', + '\172', '\126', '\040', '\163', '\172', '\040', '\061', '\012', + '\172', '\117', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\155', '\110', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\170', '\127', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\146', '\106', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\163', '\104', '\166', '\040', + '\163', '\164', '\040', '\061', '\012', '\166', '\155', '\104', + '\040', '\166', '\141', '\040', '\061', '\012', '\170', '\152', + '\114', '\040', '\151', '\152', '\040', '\061', '\012', '\151', + '\102', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\152', '\161', '\124', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\163', '\122', '\040', '\164', '\150', '\040', + '\061', '\012', '\121', '\170', '\157', '\040', '\157', '\156', + '\040', '\061', '\012', '\152', '\163', '\107', '\040', '\163', + '\164', '\040', '\061', '\012', '\143', '\130', '\142', '\040', + '\143', '\150', '\040', '\061', '\012', '\131', '\142', '\152', + '\040', '\151', '\152', '\040', '\061', '\012', '\170', '\145', + '\112', '\040', '\145', '\162', '\040', '\061', '\012', '\157', + '\120', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\171', '\130', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\166', '\114', '\040', '\166', '\141', '\040', + '\061', '\012', '\152', '\143', '\106', '\040', '\143', '\150', + '\040', '\061', '\012', '\153', '\106', '\142', '\040', '\153', + '\141', '\040', '\061', '\012', '\152', '\130', '\166', '\040', + '\151', '\152', '\040', '\061', '\012', '\101', '\157', '\170', + '\040', '\157', '\156', '\040', '\061', '\012', '\172', '\153', + '\121', '\040', '\163', '\172', '\040', '\061', '\012', '\146', + '\120', '\144', '\040', '\144', '\145', '\040', '\061', '\012', + '\106', '\166', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\146', '\142', '\130', '\040', '\142', '\145', '\040', + '\061', '\012', '\157', '\103', '\146', '\040', '\157', '\156', + '\040', '\061', '\012', '\131', '\152', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\120', '\160', '\146', '\040', + '\160', '\162', '\040', '\061', '\012', '\116', '\152', '\163', + '\040', '\163', '\164', '\040', '\061', '\012', '\143', '\132', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\166', + '\156', '\107', '\040', '\141', '\156', '\040', '\061', '\012', + '\143', '\167', '\112', '\040', '\143', '\155', '\040', '\061', + '\012', '\161', '\112', '\154', '\040', '\161', '\165', '\040', + '\061', '\012', '\147', '\116', '\146', '\040', '\156', '\147', + '\040', '\061', '\012', '\124', '\146', '\166', '\040', '\166', + '\141', '\040', '\061', '\012', '\166', '\167', '\113', '\040', + '\166', '\141', '\040', '\061', '\012', '\132', '\143', '\163', + '\040', '\143', '\150', '\040', '\061', '\012', '\145', '\102', + '\166', '\040', '\145', '\162', '\040', '\061', '\012', '\161', + '\114', '\146', '\040', '\161', '\165', '\040', '\061', '\012', + '\131', '\161', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\162', '\104', '\040', '\143', '\150', '\040', + '\061', '\012', '\111', '\143', '\152', '\040', '\143', '\150', + '\040', '\061', '\012', '\161', '\102', '\154', '\040', '\161', + '\165', '\040', '\061', '\012', '\147', '\172', '\130', '\040', + '\156', '\147', '\040', '\061', '\012', '\165', '\152', '\106', + '\040', '\161', '\165', '\040', '\061', '\012', '\166', '\170', + '\125', '\040', '\166', '\141', '\040', '\061', '\012', '\153', + '\132', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\114', '\144', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\146', '\115', '\040', '\142', '\145', '\040', + '\061', '\012', '\155', '\121', '\155', '\040', '\121', '\117', + '\040', '\061', '\012', '\172', '\154', '\121', '\040', '\154', + '\145', '\040', '\061', '\012', '\152', '\142', '\125', '\040', + '\151', '\152', '\040', '\061', '\012', '\113', '\166', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\125', '\170', + '\167', '\040', '\167', '\141', '\040', '\061', '\012', '\160', + '\152', '\123', '\040', '\151', '\152', '\040', '\061', '\012', + '\130', '\166', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\153', '\152', '\111', '\040', '\151', '\152', '\040', + '\061', '\012', '\143', '\131', '\151', '\040', '\143', '\150', + '\040', '\061', '\012', '\156', '\112', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\121', '\170', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\141', '\116', '\167', + '\040', '\141', '\156', '\040', '\061', '\012', '\112', '\146', + '\160', '\040', '\160', '\162', '\040', '\061', '\012', '\142', + '\116', '\172', '\040', '\163', '\172', '\040', '\061', '\012', + '\170', '\144', '\121', '\040', '\144', '\145', '\040', '\061', + '\012', '\102', '\172', '\153', '\040', '\163', '\172', '\040', + '\061', '\012', '\161', '\132', '\172', '\040', '\161', '\165', + '\040', '\061', '\012', '\131', '\143', '\160', '\040', '\143', + '\150', '\040', '\061', '\012', '\160', '\107', '\163', '\040', + '\163', '\164', '\040', '\061', '\012', '\153', '\103', '\146', + '\040', '\153', '\141', '\040', '\061', '\012', '\147', '\167', + '\120', '\040', '\156', '\147', '\040', '\061', '\012', '\167', + '\142', '\126', '\040', '\167', '\141', '\040', '\061', '\012', + '\105', '\161', '\164', '\040', '\145', '\161', '\040', '\061', + '\012', '\130', '\150', '\156', '\040', '\164', '\150', '\040', + '\061', '\012', '\157', '\125', '\146', '\040', '\157', '\156', + '\040', '\061', '\012', '\144', '\113', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\163', '\170', '\116', '\040', + '\163', '\164', '\040', '\061', '\012', '\117', '\146', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\147', '\103', + '\160', '\040', '\156', '\147', '\040', '\061', '\012', '\142', + '\150', '\111', '\040', '\164', '\150', '\040', '\061', '\012', + '\150', '\147', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\156', '\125', '\040', '\141', '\156', '\040', + '\061', '\012', '\153', '\152', '\124', '\040', '\151', '\152', + '\040', '\061', '\012', '\146', '\163', '\132', '\040', '\163', + '\164', '\040', '\061', '\012', '\154', '\107', '\166', '\040', + '\154', '\145', '\040', '\061', '\012', '\167', '\115', '\144', + '\040', '\144', '\145', '\040', '\061', '\012', '\165', '\153', + '\121', '\040', '\161', '\165', '\040', '\061', '\012', '\107', + '\150', '\153', '\040', '\164', '\150', '\040', '\061', '\012', + '\153', '\122', '\167', '\040', '\153', '\141', '\040', '\061', + '\012', '\172', '\122', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\147', '\167', '\113', '\040', '\156', '\147', + '\040', '\061', '\012', '\166', '\112', '\160', '\040', '\166', + '\141', '\040', '\061', '\012', '\164', '\126', '\143', '\040', + '\164', '\150', '\040', '\061', '\012', '\160', '\161', '\124', + '\040', '\161', '\165', '\040', '\061', '\012', '\151', '\131', + '\154', '\040', '\151', '\156', '\040', '\061', '\012', '\170', + '\114', '\166', '\040', '\166', '\141', '\040', '\061', '\012', + '\130', '\144', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\143', '\117', '\040', '\143', '\150', '\040', + '\061', '\012', '\160', '\154', '\115', '\040', '\154', '\145', + '\040', '\061', '\012', '\142', '\104', '\172', '\040', '\163', + '\172', '\040', '\061', '\012', '\116', '\155', '\170', '\040', + '\155', '\145', '\040', '\061', '\012', '\144', '\113', '\166', + '\040', '\144', '\145', '\040', '\061', '\012', '\150', '\120', + '\153', '\040', '\164', '\150', '\040', '\061', '\012', '\124', + '\152', '\171', '\040', '\151', '\152', '\040', '\061', '\012', + '\167', '\131', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\156', '\146', '\112', '\040', '\141', '\156', '\040', + '\061', '\012', '\164', '\146', '\103', '\040', '\164', '\150', + '\040', '\061', '\012', '\172', '\112', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\154', '\113', '\160', '\040', + '\154', '\145', '\040', '\061', '\012', '\111', '\171', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\170', '\165', + '\102', '\040', '\161', '\165', '\040', '\061', '\012', '\145', + '\113', '\170', '\040', '\145', '\162', '\040', '\061', '\012', + '\163', '\132', '\146', '\040', '\163', '\164', '\040', '\061', + '\012', '\172', '\160', '\121', '\040', '\163', '\172', '\040', + '\061', '\012', '\163', '\146', '\114', '\040', '\163', '\164', + '\040', '\061', '\012', '\155', '\152', '\124', '\040', '\151', + '\152', '\040', '\061', '\012', '\172', '\130', '\167', '\040', + '\163', '\172', '\040', '\061', '\012', '\171', '\113', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\162', '\167', + '\126', '\040', '\145', '\162', '\040', '\061', '\012', '\160', + '\152', '\102', '\040', '\151', '\152', '\040', '\061', '\012', + '\161', '\131', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\131', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\161', '\161', '\131', '\040', '\145', '\161', + '\040', '\061', '\012', '\165', '\111', '\146', '\040', '\161', + '\165', '\040', '\061', '\012', '\152', '\124', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\163', '\161', '\103', + '\040', '\161', '\165', '\040', '\061', '\012', '\165', '\112', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\144', + '\107', '\170', '\040', '\144', '\145', '\040', '\061', '\012', + '\163', '\167', '\106', '\040', '\163', '\164', '\040', '\061', + '\012', '\110', '\146', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\110', '\164', '\142', '\040', '\164', '\150', + '\040', '\061', '\012', '\160', '\146', '\127', '\040', '\150', + '\127', '\040', '\061', '\012', '\151', '\171', '\107', '\040', + '\151', '\156', '\040', '\061', '\012', '\172', '\120', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\171', '\172', + '\126', '\040', '\163', '\172', '\040', '\061', '\012', '\160', + '\126', '\172', '\040', '\163', '\172', '\040', '\061', '\012', + '\163', '\120', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\113', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\145', '\106', '\142', '\040', '\145', '\162', + '\040', '\061', '\012', '\121', '\152', '\151', '\040', '\152', + '\123', '\040', '\061', '\012', '\155', '\164', '\110', '\040', + '\164', '\150', '\040', '\061', '\012', '\167', '\147', '\132', + '\040', '\156', '\147', '\040', '\061', '\012', '\150', '\110', + '\144', '\040', '\164', '\150', '\040', '\061', '\012', '\146', + '\124', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\147', '\170', '\132', '\040', '\156', '\147', '\040', '\061', + '\012', '\113', '\164', '\147', '\040', '\164', '\150', '\040', + '\061', '\012', '\150', '\127', '\144', '\040', '\164', '\150', + '\040', '\061', '\012', '\146', '\127', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\167', '\123', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\106', '\172', '\156', + '\040', '\141', '\156', '\040', '\061', '\012', '\147', '\150', + '\110', '\040', '\164', '\150', '\040', '\061', '\012', '\156', + '\160', '\127', '\040', '\141', '\156', '\040', '\061', '\012', + '\152', '\166', '\120', '\040', '\151', '\152', '\040', '\061', + '\012', '\165', '\131', '\153', '\040', '\161', '\165', '\040', + '\061', '\012', '\125', '\170', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\123', '\161', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\172', '\143', '\112', '\040', + '\143', '\150', '\040', '\061', '\012', '\144', '\115', '\162', + '\040', '\145', '\162', '\040', '\061', '\012', '\132', '\147', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\161', + '\107', '\160', '\040', '\161', '\165', '\040', '\061', '\012', + '\157', '\126', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\125', '\141', '\040', '\141', '\156', '\040', + '\061', '\012', '\157', '\161', '\126', '\040', '\161', '\165', + '\040', '\061', '\012', '\152', '\107', '\163', '\040', '\163', + '\164', '\040', '\061', '\012', '\131', '\142', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\161', '\122', '\146', + '\040', '\161', '\165', '\040', '\061', '\012', '\142', '\162', + '\132', '\040', '\145', '\162', '\040', '\061', '\012', '\161', + '\124', '\166', '\040', '\161', '\165', '\040', '\061', '\012', + '\167', '\132', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\147', '\117', '\152', '\040', '\156', '\147', '\040', + '\061', '\012', '\112', '\152', '\151', '\040', '\151', '\156', + '\040', '\061', '\012', '\120', '\160', '\170', '\040', '\160', + '\162', '\040', '\061', '\012', '\161', '\167', '\102', '\040', + '\161', '\165', '\040', '\061', '\012', '\161', '\143', '\112', + '\040', '\143', '\150', '\040', '\061', '\012', '\146', '\106', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\167', + '\167', '\131', '\040', '\167', '\141', '\040', '\061', '\012', + '\153', '\124', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\165', '\107', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\145', '\121', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\161', '\107', '\153', '\040', '\161', + '\165', '\040', '\061', '\012', '\144', '\160', '\126', '\040', + '\144', '\145', '\040', '\061', '\012', '\166', '\124', '\155', + '\040', '\166', '\141', '\040', '\061', '\012', '\117', '\152', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\144', + '\160', '\130', '\040', '\144', '\145', '\040', '\061', '\012', + '\142', '\131', '\146', '\040', '\142', '\145', '\040', '\061', + '\012', '\164', '\152', '\126', '\040', '\164', '\150', '\040', + '\061', '\012', '\114', '\172', '\156', '\040', '\114', '\107', + '\040', '\061', '\012', '\131', '\152', '\155', '\040', '\151', + '\152', '\040', '\061', '\012', '\165', '\131', '\167', '\040', + '\161', '\165', '\040', '\061', '\012', '\132', '\144', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\150', '\130', + '\163', '\040', '\164', '\150', '\040', '\061', '\012', '\111', + '\167', '\160', '\040', '\160', '\162', '\040', '\061', '\012', + '\150', '\112', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\124', '\146', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\143', '\170', '\117', '\040', '\143', '\150', + '\040', '\061', '\012', '\121', '\161', '\171', '\040', '\161', + '\165', '\040', '\061', '\012', '\154', '\104', '\166', '\040', + '\154', '\145', '\040', '\061', '\012', '\172', '\163', '\117', + '\040', '\163', '\164', '\040', '\061', '\012', '\155', '\162', + '\107', '\040', '\145', '\162', '\040', '\061', '\012', '\143', + '\152', '\112', '\040', '\143', '\150', '\040', '\061', '\012', + '\144', '\147', '\104', '\040', '\156', '\147', '\040', '\061', + '\012', '\143', '\125', '\167', '\040', '\143', '\150', '\040', + '\061', '\012', '\172', '\144', '\102', '\040', '\144', '\145', + '\040', '\061', '\012', '\152', '\154', '\125', '\040', '\154', + '\145', '\040', '\061', '\012', '\142', '\102', '\146', '\040', + '\142', '\145', '\040', '\061', '\012', '\161', '\142', '\112', + '\040', '\161', '\165', '\040', '\061', '\012', '\161', '\154', + '\122', '\040', '\161', '\165', '\040', '\061', '\012', '\143', + '\127', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\130', '\147', '\142', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\162', '\125', '\040', '\145', '\162', '\040', + '\061', '\012', '\142', '\147', '\111', '\040', '\156', '\147', + '\040', '\061', '\012', '\167', '\152', '\112', '\040', '\151', + '\152', '\040', '\061', '\012', '\155', '\166', '\125', '\040', + '\166', '\141', '\040', '\061', '\012', '\162', '\103', '\160', + '\040', '\107', '\103', '\040', '\061', '\012', '\156', '\126', + '\170', '\040', '\141', '\156', '\040', '\061', '\012', '\170', + '\142', '\107', '\040', '\142', '\145', '\040', '\061', '\012', + '\164', '\144', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\152', '\122', '\040', '\151', '\152', '\040', + '\061', '\012', '\167', '\121', '\152', '\040', '\151', '\152', + '\040', '\061', '\012', '\170', '\172', '\132', '\040', '\163', + '\172', '\040', '\061', '\012', '\161', '\125', '\153', '\040', + '\161', '\165', '\040', '\061', '\012', '\170', '\152', '\131', + '\040', '\151', '\152', '\040', '\061', '\012', '\112', '\170', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\170', + '\132', '\163', '\040', '\163', '\164', '\040', '\061', '\012', + '\166', '\132', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\154', '\122', '\163', '\040', '\154', '\145', '\040', + '\061', '\012', '\166', '\167', '\160', '\040', '\166', '\141', + '\040', '\061', '\012', '\167', '\160', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\163', '\167', '\123', '\040', + '\163', '\164', '\040', '\061', '\012', '\105', '\161', '\170', + '\040', '\161', '\165', '\040', '\061', '\012', '\166', '\105', + '\167', '\040', '\166', '\141', '\040', '\061', '\012', '\164', + '\153', '\121', '\040', '\164', '\150', '\040', '\061', '\012', + '\166', '\147', '\130', '\040', '\156', '\147', '\040', '\061', + '\012', '\122', '\167', '\142', '\040', '\167', '\141', '\040', + '\061', '\012', '\163', '\152', '\127', '\040', '\163', '\164', + '\040', '\061', '\012', '\144', '\130', '\155', '\040', '\144', + '\145', '\040', '\061', '\012', '\146', '\166', '\131', '\040', + '\166', '\113', '\040', '\061', '\012', '\154', '\162', '\117', + '\040', '\145', '\162', '\040', '\061', '\012', '\114', '\144', + '\170', '\040', '\144', '\145', '\040', '\061', '\012', '\143', + '\170', '\126', '\040', '\143', '\150', '\040', '\061', '\012', + '\161', '\106', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\126', '\167', '\040', '\161', '\165', '\040', + '\061', '\012', '\120', '\171', '\146', '\040', '\156', '\171', + '\040', '\061', '\012', '\113', '\170', '\172', '\040', '\163', + '\172', '\040', '\061', '\012', '\150', '\167', '\112', '\040', + '\164', '\150', '\040', '\061', '\012', '\143', '\160', '\114', + '\040', '\143', '\150', '\040', '\061', '\012', '\110', '\147', + '\145', '\040', '\156', '\147', '\040', '\061', '\012', '\127', + '\142', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\154', '\121', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\104', '\154', '\040', '\164', '\150', '\040', + '\061', '\012', '\132', '\160', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\167', '\132', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\132', '\161', '\164', '\040', + '\164', '\150', '\040', '\061', '\012', '\170', '\155', '\125', + '\040', '\155', '\145', '\040', '\061', '\012', '\164', '\125', + '\146', '\040', '\164', '\150', '\040', '\061', '\012', '\161', + '\127', '\157', '\040', '\161', '\165', '\040', '\061', '\012', + '\114', '\162', '\144', '\040', '\145', '\162', '\040', '\061', + '\012', '\160', '\121', '\163', '\040', '\163', '\164', '\040', + '\061', '\012', '\162', '\132', '\166', '\040', '\145', '\162', + '\040', '\061', '\012', '\155', '\152', '\111', '\040', '\151', + '\152', '\040', '\061', '\012', '\170', '\121', '\171', '\040', + '\156', '\171', '\040', '\061', '\012', '\166', '\107', '\171', + '\040', '\166', '\141', '\040', '\061', '\012', '\152', '\167', + '\131', '\040', '\151', '\152', '\040', '\061', '\012', '\143', + '\116', '\156', '\040', '\141', '\156', '\040', '\061', '\012', + '\172', '\160', '\120', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\113', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\167', '\126', '\153', '\040', '\153', '\141', + '\040', '\061', '\012', '\164', '\115', '\150', '\040', '\143', + '\150', '\040', '\061', '\012', '\113', '\164', '\144', '\040', + '\164', '\150', '\040', '\061', '\012', '\164', '\160', '\107', + '\040', '\164', '\150', '\040', '\061', '\012', '\151', '\104', + '\146', '\040', '\151', '\156', '\040', '\061', '\012', '\161', + '\113', '\154', '\040', '\161', '\165', '\040', '\061', '\012', + '\152', '\114', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\112', '\152', '\154', '\040', '\154', '\145', '\040', + '\061', '\012', '\150', '\143', '\121', '\040', '\164', '\150', + '\040', '\061', '\012', '\124', '\161', '\147', '\040', '\161', + '\165', '\040', '\061', '\012', '\142', '\107', '\153', '\040', + '\153', '\141', '\040', '\061', '\012', '\152', '\170', '\126', + '\040', '\151', '\152', '\040', '\061', '\012', '\146', '\143', + '\103', '\040', '\143', '\150', '\040', '\061', '\012', '\106', + '\167', '\170', '\040', '\167', '\141', '\040', '\061', '\012', + '\161', '\120', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\155', '\105', '\040', '\151', '\152', '\040', + '\061', '\012', '\170', '\155', '\124', '\040', '\155', '\145', + '\040', '\061', '\012', '\154', '\170', '\103', '\040', '\107', + '\103', '\040', '\061', '\012', '\154', '\122', '\162', '\040', + '\145', '\162', '\040', '\061', '\012', '\121', '\153', '\154', + '\040', '\154', '\145', '\040', '\061', '\012', '\151', '\150', + '\106', '\040', '\164', '\150', '\040', '\061', '\012', '\114', + '\154', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\113', '\161', '\145', '\040', '\161', '\165', '\040', '\061', + '\012', '\110', '\150', '\146', '\040', '\164', '\150', '\040', + '\061', '\012', '\156', '\120', '\161', '\040', '\141', '\156', + '\040', '\061', '\012', '\172', '\166', '\121', '\040', '\121', + '\117', '\040', '\061', '\012', '\152', '\107', '\171', '\040', + '\151', '\152', '\040', '\061', '\012', '\154', '\115', '\153', + '\040', '\154', '\145', '\040', '\061', '\012', '\165', '\117', + '\152', '\040', '\161', '\165', '\040', '\061', '\012', '\146', + '\144', '\124', '\040', '\144', '\145', '\040', '\061', '\012', + '\161', '\166', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\143', '\132', '\040', '\143', '\150', '\040', + '\061', '\012', '\161', '\153', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\143', '\142', '\112', '\040', '\143', + '\150', '\040', '\061', '\012', '\147', '\146', '\113', '\040', + '\156', '\147', '\040', '\061', '\012', '\160', '\115', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\166', '\160', + '\106', '\040', '\166', '\141', '\040', '\061', '\012', '\144', + '\147', '\120', '\040', '\156', '\147', '\040', '\061', '\012', + '\155', '\170', '\106', '\040', '\155', '\145', '\040', '\061', + '\012', '\162', '\132', '\160', '\040', '\145', '\162', '\040', + '\061', '\012', '\143', '\107', '\144', '\040', '\143', '\150', + '\040', '\061', '\012', '\163', '\120', '\170', '\040', '\163', + '\164', '\040', '\061', '\012', '\162', '\107', '\144', '\040', + '\145', '\162', '\040', '\061', '\012', '\147', '\142', '\121', + '\040', '\156', '\147', '\040', '\061', '\012', '\104', '\146', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\163', + '\152', '\103', '\040', '\163', '\164', '\040', '\061', '\012', + '\172', '\123', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\111', '\157', '\040', '\161', '\165', '\040', + '\061', '\012', '\144', '\111', '\167', '\040', '\144', '\145', + '\040', '\061', '\012', '\153', '\160', '\106', '\040', '\153', + '\141', '\040', '\061', '\012', '\145', '\125', '\167', '\040', + '\145', '\162', '\040', '\061', '\012', '\110', '\170', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\171', '\166', + '\107', '\040', '\166', '\141', '\040', '\061', '\012', '\166', + '\125', '\146', '\040', '\166', '\141', '\040', '\061', '\012', + '\146', '\152', '\106', '\040', '\151', '\152', '\040', '\061', + '\012', '\153', '\114', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\132', '\152', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\146', '\114', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\171', '\144', '\123', '\040', + '\144', '\145', '\040', '\061', '\012', '\172', '\167', '\113', + '\040', '\163', '\172', '\040', '\061', '\012', '\150', '\110', + '\171', '\040', '\164', '\150', '\040', '\061', '\012', '\123', + '\163', '\167', '\040', '\163', '\164', '\040', '\061', '\012', + '\150', '\152', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\104', '\144', '\160', '\040', '\144', '\145', '\040', + '\061', '\012', '\142', '\120', '\163', '\040', '\163', '\164', + '\040', '\061', '\012', '\127', '\160', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\143', '\162', '\127', '\040', + '\143', '\150', '\040', '\061', '\012', '\130', '\160', '\152', + '\040', '\151', '\152', '\040', '\061', '\012', '\157', '\130', + '\162', '\040', '\145', '\162', '\040', '\061', '\012', '\166', + '\152', '\113', '\040', '\151', '\152', '\040', '\061', '\012', + '\126', '\172', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\131', '\144', '\040', '\154', '\145', '\040', + '\061', '\012', '\117', '\144', '\170', '\040', '\144', '\145', + '\040', '\061', '\012', '\150', '\126', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\147', '\122', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\132', '\164', '\146', + '\040', '\164', '\150', '\040', '\061', '\012', '\150', '\126', + '\152', '\040', '\164', '\150', '\040', '\061', '\012', '\112', + '\152', '\146', '\040', '\151', '\152', '\040', '\061', '\012', + '\152', '\106', '\142', '\040', '\151', '\152', '\040', '\061', + '\012', '\114', '\150', '\146', '\040', '\164', '\150', '\040', + '\061', '\012', '\152', '\154', '\117', '\040', '\154', '\145', + '\040', '\061', '\012', '\152', '\166', '\102', '\040', '\151', + '\152', '\040', '\061', '\012', '\147', '\142', '\116', '\040', + '\156', '\147', '\040', '\061', '\012', '\166', '\120', '\155', + '\040', '\166', '\141', '\040', '\061', '\012', '\164', '\121', + '\144', '\040', '\164', '\150', '\040', '\061', '\012', '\126', + '\166', '\152', '\040', '\151', '\152', '\040', '\061', '\012', + '\162', '\161', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\105', '\157', '\040', '\157', '\156', '\040', + '\061', '\012', '\152', '\163', '\102', '\040', '\163', '\164', + '\040', '\061', '\012', '\161', '\155', '\110', '\040', '\161', + '\165', '\040', '\061', '\012', '\142', '\164', '\105', '\040', + '\164', '\150', '\040', '\061', '\012', '\127', '\144', '\144', + '\040', '\144', '\145', '\040', '\061', '\012', '\104', '\155', + '\152', '\040', '\151', '\152', '\040', '\061', '\012', '\171', + '\167', '\111', '\040', '\167', '\141', '\040', '\061', '\012', + '\152', '\160', '\121', '\040', '\151', '\152', '\040', '\061', + '\012', '\165', '\130', '\163', '\040', '\161', '\165', '\040', + '\061', '\012', '\142', '\131', '\155', '\040', '\155', '\145', + '\040', '\061', '\012', '\157', '\106', '\172', '\040', '\157', + '\156', '\040', '\061', '\012', '\164', '\102', '\147', '\040', + '\164', '\150', '\040', '\061', '\012', '\143', '\103', '\156', + '\040', '\143', '\150', '\040', '\061', '\012', '\144', '\132', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\167', + '\162', '\114', '\040', '\145', '\162', '\040', '\061', '\012', + '\112', '\162', '\171', '\040', '\145', '\162', '\040', '\061', + '\012', '\151', '\113', '\144', '\040', '\151', '\156', '\040', + '\061', '\012', '\166', '\143', '\116', '\040', '\143', '\150', + '\040', '\061', '\012', '\172', '\116', '\160', '\040', '\163', + '\172', '\040', '\061', '\012', '\156', '\122', '\146', '\040', + '\141', '\156', '\040', '\061', '\012', '\144', '\143', '\110', + '\040', '\143', '\150', '\040', '\061', '\012', '\161', '\141', + '\117', '\040', '\141', '\156', '\040', '\061', '\012', '\165', + '\141', '\121', '\040', '\141', '\156', '\040', '\061', '\012', + '\152', '\170', '\114', '\040', '\151', '\152', '\040', '\061', + '\012', '\155', '\125', '\146', '\040', '\155', '\145', '\040', + '\061', '\012', '\166', '\117', '\153', '\040', '\153', '\141', + '\040', '\061', '\012', '\120', '\170', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\146', '\165', '\121', '\040', + '\161', '\165', '\040', '\061', '\012', '\163', '\146', '\116', + '\040', '\163', '\164', '\040', '\061', '\012', '\121', '\154', + '\166', '\040', '\154', '\145', '\040', '\061', '\012', '\142', + '\132', '\171', '\040', '\142', '\145', '\040', '\061', '\012', + '\166', '\105', '\161', '\040', '\166', '\113', '\040', '\061', + '\012', '\130', '\166', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\112', '\170', '\142', '\040', '\142', '\145', + '\040', '\061', '\012', '\172', '\107', '\172', '\040', '\163', + '\172', '\040', '\061', '\012', '\103', '\161', '\146', '\040', + '\161', '\165', '\040', '\061', '\012', '\163', '\120', '\160', + '\040', '\163', '\164', '\040', '\061', '\012', '\166', '\101', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\153', + '\127', '\144', '\040', '\144', '\145', '\040', '\061', '\012', + '\162', '\143', '\132', '\040', '\143', '\155', '\040', '\061', + '\012', '\154', '\104', '\163', '\040', '\154', '\145', '\040', + '\061', '\012', '\170', '\104', '\144', '\040', '\144', '\145', + '\040', '\061', '\012', '\160', '\123', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\166', '\167', '\123', '\040', + '\166', '\141', '\040', '\061', '\012', '\153', '\147', '\121', + '\040', '\156', '\147', '\040', '\061', '\012', '\143', '\162', + '\124', '\040', '\143', '\150', '\040', '\061', '\012', '\146', + '\113', '\163', '\040', '\163', '\164', '\040', '\061', '\012', + '\161', '\150', '\143', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\115', '\154', '\040', '\156', '\147', '\040', + '\061', '\012', '\172', '\113', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\152', '\144', '\106', '\040', '\144', + '\145', '\040', '\061', '\012', '\143', '\146', '\116', '\040', + '\143', '\150', '\040', '\061', '\012', '\163', '\144', '\117', + '\040', '\163', '\164', '\040', '\061', '\012', '\153', '\110', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\170', + '\166', '\105', '\040', '\166', '\141', '\040', '\061', '\012', + '\142', '\120', '\146', '\040', '\142', '\145', '\040', '\061', + '\012', '\162', '\172', '\130', '\040', '\145', '\162', '\040', + '\061', '\012', '\166', '\123', '\152', '\040', '\151', '\152', + '\040', '\061', '\012', '\144', '\106', '\146', '\040', '\144', + '\145', '\040', '\061', '\012', '\166', '\130', '\154', '\040', + '\154', '\145', '\040', '\061', '\012', '\142', '\122', '\166', + '\040', '\166', '\141', '\040', '\061', '\012', '\132', '\170', + '\167', '\040', '\167', '\141', '\040', '\061', '\012', '\130', + '\172', '\167', '\040', '\163', '\172', '\040', '\061', '\012', + '\166', '\162', '\122', '\040', '\145', '\162', '\040', '\061', + '\012', '\170', '\110', '\142', '\040', '\142', '\145', '\040', + '\061', '\012', '\161', '\145', '\105', '\040', '\161', '\165', + '\040', '\061', '\012', '\152', '\162', '\121', '\040', '\145', + '\162', '\040', '\061', '\012', '\166', '\153', '\111', '\040', + '\153', '\141', '\040', '\061', '\012', '\146', '\162', '\131', + '\040', '\145', '\162', '\040', '\061', '\012', '\152', '\161', + '\114', '\040', '\161', '\165', '\040', '\061', '\012', '\143', + '\132', '\152', '\040', '\143', '\150', '\040', '\061', '\012', + '\124', '\155', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\155', '\110', '\167', '\040', '\155', '\145', '\040', + '\061', '\012', '\144', '\161', '\123', '\040', '\161', '\165', + '\040', '\061', '\012', '\161', '\154', '\111', '\040', '\161', + '\165', '\040', '\061', '\012', '\132', '\166', '\142', '\040', + '\166', '\141', '\040', '\061', '\012', '\113', '\154', '\170', + '\040', '\154', '\145', '\040', '\061', '\012', '\147', '\142', + '\123', '\040', '\156', '\147', '\040', '\061', '\012', '\163', + '\142', '\121', '\040', '\163', '\164', '\040', '\061', '\012', + '\161', '\165', '\106', '\040', '\165', '\156', '\040', '\061', + '\012', '\161', '\172', '\124', '\040', '\161', '\165', '\040', + '\061', '\012', '\161', '\141', '\111', '\040', '\141', '\156', + '\040', '\061', '\012', '\126', '\155', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\161', '\141', '\121', '\040', + '\141', '\156', '\040', '\061', '\012', '\121', '\153', '\142', + '\040', '\153', '\141', '\040', '\061', '\012', '\130', '\152', + '\142', '\040', '\151', '\152', '\040', '\061', '\012', '\157', + '\103', '\161', '\040', '\107', '\103', '\040', '\061', '\012', + '\161', '\121', '\150', '\040', '\121', '\117', '\040', '\061', + '\012', '\143', '\167', '\117', '\040', '\143', '\150', '\040', + '\061', '\012', '\164', '\115', '\146', '\040', '\164', '\150', + '\040', '\061', '\012', '\172', '\162', '\113', '\040', '\145', + '\162', '\040', '\061', '\012', '\167', '\113', '\171', '\040', + '\167', '\141', '\040', '\061', '\012', '\167', '\113', '\142', + '\040', '\167', '\141', '\040', '\061', '\012', '\143', '\161', + '\123', '\040', '\143', '\150', '\040', '\061', '\012', '\151', + '\107', '\166', '\040', '\151', '\156', '\040', '\061', '\012', + '\170', '\130', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\146', '\115', '\170', '\040', '\146', '\157', '\040', + '\061', '\012', '\132', '\155', '\166', '\040', '\166', '\141', + '\040', '\061', '\012', '\131', '\161', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\153', '\104', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\112', '\170', '\171', + '\040', '\156', '\171', '\040', '\061', '\012', '\171', '\171', + '\105', '\040', '\156', '\171', '\040', '\061', '\012', '\163', + '\125', '\166', '\040', '\163', '\164', '\040', '\061', '\012', + '\143', '\126', '\162', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\161', '\110', '\040', '\161', '\165', '\040', + '\061', '\012', '\127', '\147', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\165', '\161', '\121', '\040', '\161', + '\165', '\040', '\061', '\012', '\142', '\124', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\151', '\115', '\166', + '\040', '\151', '\156', '\040', '\061', '\012', '\161', '\127', + '\153', '\040', '\161', '\165', '\040', '\061', '\012', '\146', + '\144', '\126', '\040', '\144', '\145', '\040', '\061', '\012', + '\157', '\121', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\132', '\160', '\040', '\141', '\156', '\040', + '\061', '\012', '\172', '\157', '\131', '\040', '\157', '\156', + '\040', '\061', '\012', '\152', '\122', '\153', '\040', '\151', + '\152', '\040', '\061', '\012', '\161', '\120', '\152', '\040', + '\161', '\165', '\040', '\061', '\012', '\165', '\161', '\114', + '\040', '\161', '\165', '\040', '\061', '\012', '\143', '\161', + '\130', '\040', '\143', '\150', '\040', '\061', '\012', '\154', + '\102', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\146', '\160', '\130', '\040', '\160', '\162', '\040', '\061', + '\012', '\142', '\131', '\167', '\040', '\167', '\141', '\040', + '\061', '\012', '\131', '\145', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\150', '\152', '\116', '\040', '\164', + '\150', '\040', '\061', '\012', '\164', '\161', '\127', '\040', + '\164', '\150', '\040', '\061', '\012', '\152', '\150', '\124', + '\040', '\164', '\150', '\040', '\061', '\012', '\143', '\166', + '\106', '\040', '\143', '\150', '\040', '\061', '\012', '\131', + '\143', '\170', '\040', '\143', '\150', '\040', '\061', '\012', + '\152', '\106', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\110', '\144', '\171', '\040', '\144', '\145', '\040', + '\061', '\012', '\154', '\162', '\132', '\040', '\145', '\162', + '\040', '\061', '\012', '\146', '\132', '\166', '\040', '\166', + '\141', '\040', '\061', '\012', '\124', '\146', '\167', '\040', + '\167', '\141', '\040', '\061', '\012', '\172', '\162', '\111', + '\040', '\145', '\162', '\040', '\061', '\012', '\144', '\104', + '\166', '\040', '\144', '\145', '\040', '\061', '\012', '\170', + '\145', '\110', '\040', '\145', '\162', '\040', '\061', '\012', + '\154', '\172', '\110', '\040', '\154', '\145', '\040', '\061', + '\012', '\163', '\114', '\162', '\040', '\145', '\162', '\040', + '\061', '\012', '\151', '\113', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\106', '\172', '\143', '\040', '\143', + '\155', '\040', '\061', '\012', '\170', '\122', '\144', '\040', + '\144', '\145', '\040', '\061', '\012', '\146', '\123', '\144', + '\040', '\144', '\145', '\040', '\061', '\012', '\161', '\167', + '\106', '\040', '\161', '\165', '\040', '\061', '\012', '\167', + '\170', '\131', '\040', '\167', '\141', '\040', '\061', '\012', + '\131', '\153', '\167', '\040', '\153', '\141', '\040', '\061', + '\012', '\157', '\126', '\160', '\040', '\157', '\156', '\040', + '\061', '\012', '\143', '\147', '\102', '\040', '\143', '\150', + '\040', '\061', '\012', '\142', '\106', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\156', '\152', '\124', '\040', + '\141', '\156', '\040', '\061', '\012', '\144', '\132', '\172', + '\040', '\144', '\145', '\040', '\061', '\012', '\142', '\150', + '\123', '\040', '\164', '\150', '\040', '\061', '\012', '\106', + '\172', '\165', '\040', '\161', '\165', '\040', '\061', '\012', + '\146', '\110', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\166', '\116', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\161', '\154', '\106', '\040', '\161', '\165', + '\040', '\061', '\012', '\114', '\166', '\146', '\040', '\166', + '\141', '\040', '\061', '\012', '\172', '\160', '\125', '\040', + '\163', '\172', '\040', '\061', '\012', '\152', '\164', '\114', + '\040', '\164', '\150', '\040', '\061', '\012', '\143', '\121', + '\161', '\040', '\143', '\150', '\040', '\061', '\012', '\155', + '\113', '\155', '\040', '\155', '\145', '\040', '\061', '\012', + '\122', '\167', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\162', '\117', '\040', '\145', '\162', '\040', + '\061', '\012', '\156', '\160', '\102', '\040', '\141', '\156', + '\040', '\061', '\012', '\121', '\164', '\170', '\040', '\164', + '\150', '\040', '\061', '\012', '\115', '\161', '\152', '\040', + '\161', '\165', '\040', '\061', '\012', '\117', '\161', '\170', + '\040', '\161', '\165', '\040', '\061', '\012', '\104', '\172', + '\160', '\040', '\163', '\172', '\040', '\061', '\012', '\150', + '\126', '\147', '\040', '\164', '\150', '\040', '\061', '\012', + '\160', '\124', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\121', '\152', '\040', '\156', '\147', '\040', + '\061', '\012', '\155', '\124', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\164', '\121', '\166', '\040', '\164', + '\150', '\040', '\061', '\012', '\154', '\132', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\153', '\112', '\152', + '\040', '\151', '\152', '\040', '\061', '\012', '\143', '\162', + '\120', '\040', '\143', '\150', '\040', '\061', '\012', '\155', + '\161', '\103', '\040', '\161', '\165', '\040', '\061', '\012', + '\104', '\167', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\126', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\150', '\161', '\124', '\040', '\164', '\150', + '\040', '\061', '\012', '\155', '\112', '\167', '\040', '\155', + '\145', '\040', '\061', '\012', '\164', '\170', '\124', '\040', + '\164', '\150', '\040', '\061', '\012', '\167', '\132', '\155', + '\040', '\155', '\145', '\040', '\061', '\012', '\130', '\156', + '\161', '\040', '\141', '\156', '\040', '\061', '\012', '\150', + '\146', '\125', '\040', '\164', '\150', '\040', '\061', '\012', + '\153', '\126', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\147', '\126', '\160', '\040', '\156', '\147', '\040', + '\061', '\012', '\156', '\102', '\160', '\040', '\141', '\156', + '\040', '\061', '\012', '\170', '\156', '\132', '\040', '\141', + '\156', '\040', '\061', '\012', '\152', '\161', '\101', '\040', + '\161', '\165', '\040', '\061', '\012', '\120', '\172', '\153', + '\040', '\163', '\172', '\040', '\061', '\012', '\146', '\112', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\107', + '\156', '\146', '\040', '\141', '\156', '\040', '\061', '\012', + '\113', '\170', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\144', '\130', '\154', '\040', '\130', '\155', '\040', + '\061', '\012', '\150', '\167', '\114', '\040', '\164', '\150', + '\040', '\061', '\012', '\122', '\162', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\153', '\154', '\114', '\040', + '\154', '\145', '\040', '\061', '\012', '\146', '\117', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\121', '\167', + '\170', '\040', '\167', '\141', '\040', '\061', '\012', '\103', + '\155', '\170', '\040', '\155', '\145', '\040', '\061', '\012', + '\106', '\142', '\146', '\040', '\142', '\145', '\040', '\061', + '\012', '\150', '\127', '\161', '\040', '\164', '\150', '\040', + '\061', '\012', '\142', '\123', '\167', '\040', '\167', '\141', + '\040', '\061', '\012', '\102', '\170', '\162', '\040', '\145', + '\162', '\040', '\061', '\012', '\172', '\143', '\102', '\040', + '\143', '\150', '\040', '\061', '\012', '\154', '\166', '\130', + '\040', '\154', '\145', '\040', '\061', '\012', '\113', '\153', + '\170', '\040', '\153', '\141', '\040', '\061', '\012', '\161', + '\146', '\111', '\040', '\161', '\165', '\040', '\061', '\012', + '\165', '\113', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\131', '\153', '\165', '\040', '\161', '\165', '\040', + '\061', '\012', '\152', '\112', '\172', '\040', '\163', '\172', + '\040', '\061', '\012', '\165', '\111', '\160', '\040', '\161', + '\165', '\040', '\061', '\012', '\161', '\101', '\144', '\040', + '\161', '\165', '\040', '\061', '\012', '\160', '\146', '\110', + '\040', '\160', '\162', '\040', '\061', '\012', '\121', '\167', + '\146', '\040', '\167', '\141', '\040', '\061', '\012', '\167', + '\142', '\125', '\040', '\167', '\141', '\040', '\061', '\012', + '\166', '\104', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\147', '\112', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\172', '\154', '\122', '\040', '\154', '\145', + '\040', '\061', '\012', '\155', '\130', '\162', '\040', '\145', + '\162', '\040', '\061', '\012', '\162', '\110', '\170', '\040', + '\145', '\162', '\040', '\061', '\012', '\157', '\126', '\172', + '\040', '\157', '\156', '\040', '\061', '\012', '\147', '\164', + '\107', '\040', '\164', '\150', '\040', '\061', '\012', '\154', + '\162', '\113', '\040', '\110', '\113', '\040', '\061', '\012', + '\127', '\170', '\145', '\040', '\145', '\162', '\040', '\061', + '\012', '\160', '\156', '\112', '\040', '\141', '\156', '\040', + '\061', '\012', '\106', '\161', '\171', '\040', '\161', '\165', + '\040', '\061', '\012', '\152', '\126', '\154', '\040', '\154', + '\145', '\040', '\061', '\012', '\143', '\142', '\120', '\040', + '\143', '\150', '\040', '\061', '\012', '\107', '\152', '\143', + '\040', '\152', '\123', '\040', '\061', '\012', '\152', '\121', + '\163', '\040', '\163', '\164', '\040', '\061', '\012', '\164', + '\166', '\126', '\040', '\164', '\150', '\040', '\061', '\012', + '\110', '\172', '\153', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\171', '\127', '\040', '\151', '\152', '\040', + '\061', '\012', '\130', '\142', '\146', '\040', '\142', '\145', + '\040', '\061', '\012', '\161', '\146', '\123', '\040', '\161', + '\165', '\040', '\061', '\012', '\127', '\166', '\160', '\040', + '\166', '\141', '\040', '\061', '\012', '\167', '\142', '\114', + '\040', '\167', '\141', '\040', '\061', '\012', '\155', '\153', + '\117', '\040', '\153', '\141', '\040', '\061', '\012', '\145', + '\161', '\102', '\040', '\161', '\165', '\040', '\061', '\012', + '\144', '\166', '\123', '\040', '\144', '\145', '\040', '\061', + '\012', '\172', '\107', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\166', '\127', '\165', '\040', '\161', '\165', + '\040', '\061', '\012', '\146', '\154', '\130', '\040', '\154', + '\145', '\040', '\061', '\012', '\170', '\112', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\161', '\114', '\153', + '\040', '\161', '\165', '\040', '\061', '\012', '\166', '\116', + '\154', '\040', '\154', '\145', '\040', '\061', '\012', '\153', + '\172', '\121', '\040', '\163', '\172', '\040', '\061', '\012', + '\103', '\172', '\166', '\040', '\163', '\172', '\040', '\061', + '\012', '\153', '\156', '\126', '\040', '\141', '\156', '\040', + '\061', '\012', '\122', '\152', '\142', '\040', '\151', '\152', + '\040', '\061', '\012', '\142', '\116', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\172', '\120', '\155', '\040', + '\163', '\172', '\040', '\061', '\012', '\161', '\170', '\102', + '\040', '\161', '\165', '\040', '\061', '\012', '\114', '\150', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\125', + '\166', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\170', '\146', '\125', '\040', '\146', '\157', '\040', '\061', + '\012', '\151', '\116', '\160', '\040', '\151', '\156', '\040', + '\061', '\012', '\171', '\131', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\157', '\120', '\142', '\040', '\157', + '\156', '\040', '\061', '\012', '\161', '\151', '\127', '\040', + '\161', '\165', '\040', '\061', '\012', '\171', '\143', '\104', + '\040', '\143', '\150', '\040', '\061', '\012', '\167', '\126', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\167', + '\107', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\150', '\122', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\142', '\102', '\040', '\142', '\145', '\040', + '\061', '\012', '\163', '\132', '\154', '\040', '\154', '\145', + '\040', '\061', '\012', '\147', '\170', '\117', '\040', '\156', + '\147', '\040', '\061', '\012', '\167', '\106', '\153', '\040', + '\153', '\141', '\040', '\061', '\012', '\115', '\170', '\144', + '\040', '\144', '\145', '\040', '\061', '\012', '\144', '\170', + '\120', '\040', '\144', '\145', '\040', '\061', '\012', '\154', + '\122', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\150', '\142', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\105', '\141', '\157', '\040', '\141', '\156', '\040', + '\061', '\012', '\172', '\147', '\101', '\040', '\156', '\147', + '\040', '\061', '\012', '\161', '\143', '\127', '\040', '\143', + '\150', '\040', '\061', '\012', '\166', '\155', '\121', '\040', + '\166', '\141', '\040', '\061', '\012', '\131', '\161', '\146', + '\040', '\161', '\165', '\040', '\061', '\012', '\167', '\151', + '\117', '\040', '\151', '\156', '\040', '\061', '\012', '\170', + '\117', '\145', '\040', '\145', '\162', '\040', '\061', '\012', + '\110', '\146', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\142', '\146', '\123', '\040', '\142', '\145', '\040', + '\061', '\012', '\121', '\150', '\156', '\040', '\164', '\150', + '\040', '\061', '\012', '\103', '\155', '\153', '\040', '\153', + '\141', '\040', '\061', '\012', '\154', '\131', '\163', '\040', + '\154', '\145', '\040', '\061', '\012', '\116', '\161', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\161', '\145', + '\112', '\040', '\161', '\165', '\040', '\061', '\012', '\172', + '\164', '\112', '\040', '\164', '\150', '\040', '\061', '\012', + '\160', '\115', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\165', '\150', '\127', '\040', '\164', '\150', '\040', + '\061', '\012', '\152', '\123', '\142', '\040', '\151', '\152', + '\040', '\061', '\012', '\144', '\131', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\143', '\146', '\127', '\040', + '\143', '\150', '\040', '\061', '\012', '\147', '\123', '\170', + '\040', '\156', '\147', '\040', '\061', '\012', '\161', '\123', + '\166', '\040', '\161', '\165', '\040', '\061', '\012', '\152', + '\103', '\163', '\040', '\163', '\164', '\040', '\061', '\012', + '\160', '\167', '\103', '\040', '\160', '\162', '\040', '\061', + '\012', '\107', '\170', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\146', '\115', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\153', '\153', '\103', '\040', '\153', + '\141', '\040', '\061', '\012', '\165', '\161', '\111', '\040', + '\161', '\165', '\040', '\061', '\012', '\172', '\102', '\153', + '\040', '\163', '\172', '\040', '\061', '\012', '\172', '\163', + '\127', '\040', '\163', '\164', '\040', '\061', '\012', '\146', + '\132', '\142', '\040', '\142', '\145', '\040', '\061', '\012', + '\170', '\152', '\142', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\110', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\146', '\167', '\116', '\040', '\167', '\141', + '\040', '\061', '\012', '\166', '\115', '\167', '\040', '\166', + '\141', '\040', '\061', '\012', '\110', '\150', '\161', '\040', + '\164', '\150', '\040', '\061', '\012', '\143', '\163', '\112', + '\040', '\143', '\150', '\040', '\061', '\012', '\142', '\162', + '\112', '\040', '\145', '\162', '\040', '\061', '\012', '\170', + '\166', '\115', '\040', '\166', '\141', '\040', '\061', '\012', + '\155', '\130', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\127', '\167', '\040', '\167', '\141', '\040', + '\061', '\012', '\144', '\170', '\132', '\040', '\144', '\145', + '\040', '\061', '\012', '\163', '\126', '\152', '\040', '\163', + '\164', '\040', '\061', '\012', '\170', '\162', '\106', '\040', + '\145', '\162', '\040', '\061', '\012', '\160', '\142', '\125', + '\040', '\160', '\162', '\040', '\061', '\012', '\124', '\146', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\167', + '\161', '\124', '\040', '\161', '\165', '\040', '\061', '\012', + '\166', '\143', '\106', '\040', '\143', '\150', '\040', '\061', + '\012', '\156', '\162', '\123', '\040', '\141', '\156', '\040', + '\061', '\012', '\127', '\150', '\172', '\040', '\164', '\150', + '\040', '\061', '\012', '\153', '\147', '\130', '\040', '\156', + '\147', '\040', '\061', '\012', '\171', '\130', '\153', '\040', + '\153', '\141', '\040', '\061', '\012', '\153', '\112', '\142', + '\040', '\153', '\141', '\040', '\061', '\012', '\162', '\132', + '\153', '\040', '\145', '\162', '\040', '\061', '\012', '\160', + '\102', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\147', '\125', '\166', '\040', '\156', '\147', '\040', '\061', + '\012', '\110', '\161', '\145', '\040', '\161', '\165', '\040', + '\061', '\012', '\113', '\161', '\152', '\040', '\161', '\165', + '\040', '\061', '\012', '\157', '\106', '\152', '\040', '\157', + '\156', '\040', '\061', '\012', '\170', '\142', '\116', '\040', + '\142', '\145', '\040', '\061', '\012', '\160', '\156', '\113', + '\040', '\141', '\156', '\040', '\061', '\012', '\114', '\142', + '\167', '\040', '\167', '\141', '\040', '\061', '\012', '\144', + '\115', '\142', '\040', '\144', '\145', '\040', '\061', '\012', + '\161', '\123', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\163', '\166', '\040', '\163', '\164', '\040', + '\061', '\012', '\167', '\162', '\126', '\040', '\145', '\162', + '\040', '\061', '\012', '\165', '\113', '\146', '\040', '\161', + '\165', '\040', '\061', '\012', '\155', '\154', '\131', '\040', + '\154', '\145', '\040', '\061', '\012', '\147', '\170', '\106', + '\040', '\156', '\147', '\040', '\061', '\012', '\164', '\152', + '\114', '\040', '\164', '\150', '\040', '\061', '\012', '\130', + '\162', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\162', '\166', '\106', '\040', '\145', '\162', '\040', '\061', + '\012', '\155', '\114', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\152', '\162', '\113', '\040', '\145', '\162', + '\040', '\061', '\012', '\121', '\154', '\172', '\040', '\154', + '\145', '\040', '\061', '\012', '\172', '\170', '\104', '\040', + '\163', '\172', '\040', '\061', '\012', '\146', '\144', '\131', + '\040', '\144', '\145', '\040', '\061', '\012', '\152', '\166', + '\104', '\040', '\151', '\152', '\040', '\061', '\012', '\170', + '\121', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\161', '\106', '\165', '\040', '\165', '\156', '\040', '\061', + '\012', '\163', '\146', '\112', '\040', '\163', '\164', '\040', + '\061', '\012', '\160', '\111', '\146', '\040', '\160', '\162', + '\040', '\061', '\012', '\150', '\170', '\112', '\040', '\164', + '\150', '\040', '\061', '\012', '\143', '\116', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\111', '\144', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\171', '\110', + '\146', '\040', '\156', '\171', '\040', '\061', '\012', '\161', + '\130', '\155', '\040', '\161', '\165', '\040', '\061', '\012', + '\171', '\154', '\104', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\106', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\152', '\127', '\160', '\040', '\151', '\152', + '\040', '\061', '\012', '\145', '\113', '\160', '\040', '\145', + '\162', '\040', '\061', '\012', '\170', '\150', '\146', '\040', + '\164', '\150', '\040', '\061', '\012', '\171', '\142', '\126', + '\040', '\142', '\145', '\040', '\061', '\012', '\170', '\130', + '\163', '\040', '\163', '\164', '\040', '\061', '\012', '\131', + '\150', '\153', '\040', '\164', '\150', '\040', '\061', '\012', + '\146', '\167', '\130', '\040', '\167', '\141', '\040', '\061', + '\012', '\142', '\161', '\113', '\040', '\161', '\165', '\040', + '\061', '\012', '\156', '\166', '\131', '\040', '\141', '\156', + '\040', '\061', '\012', '\170', '\166', '\153', '\040', '\153', + '\141', '\040', '\061', '\012', '\162', '\142', '\120', '\040', + '\145', '\162', '\040', '\061', '\012', '\163', '\130', '\154', + '\040', '\154', '\145', '\040', '\061', '\012', '\125', '\167', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\167', + '\155', '\127', '\040', '\155', '\145', '\040', '\061', '\012', + '\160', '\170', '\126', '\040', '\160', '\162', '\040', '\061', + '\012', '\156', '\152', '\132', '\040', '\141', '\156', '\040', + '\061', '\012', '\124', '\161', '\153', '\040', '\161', '\165', + '\040', '\061', '\012', '\172', '\155', '\105', '\040', '\163', + '\172', '\040', '\061', '\012', '\122', '\161', '\165', '\040', + '\165', '\156', '\040', '\061', '\012', '\161', '\161', '\115', + '\040', '\161', '\165', '\040', '\061', '\012', '\144', '\150', + '\121', '\040', '\164', '\150', '\040', '\061', '\012', '\165', + '\112', '\172', '\040', '\161', '\165', '\040', '\061', '\012', + '\126', '\161', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\103', '\153', '\040', '\153', '\141', '\040', + '\061', '\012', '\160', '\127', '\165', '\040', '\161', '\165', + '\040', '\061', '\012', '\126', '\144', '\171', '\040', '\144', + '\145', '\040', '\061', '\012', '\151', '\122', '\170', '\040', + '\151', '\156', '\040', '\061', '\012', '\126', '\143', '\155', + '\040', '\143', '\150', '\040', '\061', '\012', '\167', '\111', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\130', + '\142', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\166', '\143', '\107', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\152', '\130', '\040', '\151', '\152', '\040', + '\061', '\012', '\156', '\155', '\117', '\040', '\141', '\156', + '\040', '\061', '\012', '\144', '\121', '\152', '\040', '\144', + '\145', '\040', '\061', '\012', '\144', '\146', '\126', '\040', + '\144', '\145', '\040', '\061', '\012', '\144', '\142', '\113', + '\040', '\144', '\145', '\040', '\061', '\012', '\147', '\161', + '\153', '\040', '\161', '\165', '\040', '\061', '\012', '\156', + '\106', '\144', '\040', '\141', '\156', '\040', '\061', '\012', + '\157', '\127', '\166', '\040', '\157', '\156', '\040', '\061', + '\012', '\156', '\110', '\160', '\040', '\141', '\156', '\040', + '\061', '\012', '\153', '\156', '\113', '\040', '\141', '\156', + '\040', '\061', '\012', '\142', '\170', '\132', '\040', '\142', + '\145', '\040', '\061', '\012', '\167', '\155', '\110', '\040', + '\155', '\145', '\040', '\061', '\012', '\146', '\147', '\130', + '\040', '\156', '\147', '\040', '\061', '\012', '\147', '\172', + '\110', '\040', '\156', '\147', '\040', '\061', '\012', '\132', + '\142', '\166', '\040', '\166', '\141', '\040', '\061', '\012', + '\166', '\147', '\115', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\155', '\113', '\040', '\144', '\145', '\040', + '\061', '\012', '\143', '\166', '\102', '\040', '\143', '\150', + '\040', '\061', '\012', '\145', '\121', '\163', '\040', '\145', + '\162', '\040', '\061', '\012', '\143', '\110', '\155', '\040', + '\143', '\150', '\040', '\061', '\012', '\163', '\102', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\142', '\110', + '\170', '\040', '\142', '\145', '\040', '\061', '\012', '\166', + '\161', '\144', '\040', '\161', '\165', '\040', '\061', '\012', + '\116', '\160', '\171', '\040', '\160', '\162', '\040', '\061', + '\012', '\170', '\172', '\114', '\040', '\163', '\172', '\040', + '\061', '\012', '\147', '\115', '\170', '\040', '\156', '\147', + '\040', '\061', '\012', '\166', '\167', '\125', '\040', '\166', + '\141', '\040', '\061', '\012', '\160', '\146', '\130', '\040', + '\160', '\162', '\040', '\061', '\012', '\156', '\106', '\147', + '\040', '\141', '\156', '\040', '\061', '\012', '\163', '\106', + '\163', '\040', '\163', '\164', '\040', '\061', '\012', '\126', + '\161', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\105', '\155', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\130', '\171', '\040', '\164', '\150', '\040', + '\061', '\012', '\165', '\126', '\144', '\040', '\161', '\165', + '\040', '\061', '\012', '\131', '\166', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\161', '\110', '\157', '\040', + '\161', '\165', '\040', '\061', '\012', '\160', '\127', '\155', + '\040', '\155', '\145', '\040', '\061', '\012', '\170', '\143', + '\113', '\040', '\143', '\150', '\040', '\061', '\012', '\160', + '\125', '\166', '\040', '\166', '\141', '\040', '\061', '\012', + '\160', '\114', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\165', '\126', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\106', '\163', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\143', '\107', '\152', '\040', '\143', + '\150', '\040', '\061', '\012', '\130', '\167', '\171', '\040', + '\167', '\141', '\040', '\061', '\012', '\147', '\172', '\124', + '\040', '\156', '\147', '\040', '\061', '\012', '\144', '\116', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\152', + '\162', '\125', '\040', '\145', '\162', '\040', '\061', '\012', + '\161', '\164', '\101', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\161', '\124', '\040', '\161', '\165', '\040', + '\061', '\012', '\160', '\167', '\115', '\040', '\160', '\162', + '\040', '\061', '\012', '\154', '\162', '\120', '\040', '\145', + '\162', '\040', '\061', '\012', '\152', '\155', '\103', '\040', + '\151', '\152', '\040', '\061', '\012', '\160', '\155', '\120', + '\040', '\155', '\145', '\040', '\061', '\012', '\171', '\151', + '\131', '\040', '\151', '\156', '\040', '\061', '\012', '\160', + '\124', '\163', '\040', '\163', '\164', '\040', '\061', '\012', + '\132', '\167', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\160', '\106', '\040', '\161', '\165', '\040', + '\061', '\012', '\146', '\150', '\112', '\040', '\143', '\150', + '\040', '\061', '\012', '\146', '\117', '\166', '\040', '\166', + '\141', '\040', '\061', '\012', '\167', '\143', '\113', '\040', + '\143', '\150', '\040', '\061', '\012', '\153', '\161', '\153', + '\040', '\161', '\165', '\040', '\061', '\012', '\125', '\147', + '\172', '\040', '\156', '\147', '\040', '\061', '\012', '\170', + '\146', '\106', '\040', '\146', '\157', '\040', '\061', '\012', + '\143', '\124', '\166', '\040', '\143', '\150', '\040', '\061', + '\012', '\147', '\160', '\130', '\040', '\156', '\147', '\040', + '\061', '\012', '\114', '\146', '\170', '\040', '\146', '\157', + '\040', '\061', '\012', '\147', '\167', '\125', '\040', '\156', + '\147', '\040', '\061', '\012', '\104', '\172', '\170', '\040', + '\163', '\172', '\040', '\061', '\012', '\153', '\104', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\120', '\166', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\153', + '\144', '\131', '\040', '\144', '\145', '\040', '\061', '\012', + '\167', '\127', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\163', '\121', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\155', '\152', '\131', '\040', '\151', '\152', + '\040', '\061', '\012', '\171', '\103', '\142', '\040', '\142', + '\145', '\040', '\061', '\012', '\162', '\123', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\123', '\146', '\166', + '\040', '\166', '\141', '\040', '\061', '\012', '\146', '\132', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\144', + '\115', '\144', '\040', '\144', '\145', '\040', '\061', '\012', + '\144', '\116', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\152', '\124', '\166', '\040', '\151', '\152', '\040', + '\061', '\012', '\164', '\155', '\127', '\040', '\164', '\150', + '\040', '\061', '\012', '\143', '\170', '\112', '\040', '\143', + '\150', '\040', '\061', '\012', '\165', '\101', '\157', '\040', + '\161', '\165', '\040', '\061', '\012', '\155', '\110', '\170', + '\040', '\155', '\145', '\040', '\061', '\012', '\146', '\147', + '\101', '\040', '\156', '\147', '\040', '\061', '\012', '\122', + '\150', '\170', '\040', '\164', '\150', '\040', '\061', '\012', + '\167', '\127', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\146', '\125', '\040', '\160', '\162', '\040', + '\061', '\012', '\157', '\111', '\152', '\040', '\157', '\156', + '\040', '\061', '\012', '\154', '\150', '\121', '\040', '\164', + '\150', '\040', '\061', '\012', '\166', '\104', '\153', '\040', + '\153', '\141', '\040', '\061', '\012', '\166', '\112', '\144', + '\040', '\144', '\145', '\040', '\061', '\012', '\163', '\104', + '\160', '\040', '\163', '\164', '\040', '\061', '\012', '\161', + '\151', '\125', '\040', '\161', '\165', '\040', '\061', '\012', + '\131', '\146', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\161', '\170', '\127', '\040', '\161', '\165', '\040', + '\061', '\012', '\163', '\106', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\166', '\150', '\120', '\040', '\164', + '\150', '\040', '\061', '\012', '\126', '\152', '\152', '\040', + '\151', '\152', '\040', '\061', '\012', '\164', '\155', '\121', + '\040', '\164', '\150', '\040', '\061', '\012', '\167', '\155', + '\115', '\040', '\155', '\145', '\040', '\061', '\012', '\143', + '\126', '\171', '\040', '\143', '\150', '\040', '\061', '\012', + '\113', '\172', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\164', '\146', '\101', '\040', '\164', '\150', '\040', + '\061', '\012', '\147', '\152', '\122', '\040', '\151', '\152', + '\040', '\061', '\012', '\170', '\171', '\121', '\040', '\156', + '\171', '\040', '\061', '\012', '\155', '\102', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\146', '\121', '\171', + '\040', '\156', '\171', '\040', '\061', '\012', '\144', '\132', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\145', + '\126', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\116', '\166', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\106', '\142', '\040', '\161', '\165', '\040', + '\061', '\012', '\161', '\150', '\154', '\040', '\164', '\150', + '\040', '\061', '\012', '\132', '\143', '\156', '\040', '\143', + '\150', '\040', '\061', '\012', '\161', '\167', '\127', '\040', + '\161', '\165', '\040', '\061', '\012', '\170', '\132', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\152', '\150', + '\114', '\040', '\164', '\150', '\040', '\061', '\012', '\154', + '\127', '\146', '\040', '\154', '\145', '\040', '\061', '\012', + '\152', '\112', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\131', '\172', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\105', '\157', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\116', '\152', '\155', '\040', '\151', + '\152', '\040', '\061', '\012', '\132', '\147', '\144', '\040', + '\156', '\147', '\040', '\061', '\012', '\160', '\107', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\163', '\147', + '\131', '\040', '\156', '\147', '\040', '\061', '\012', '\152', + '\171', '\105', '\040', '\151', '\152', '\040', '\061', '\012', + '\152', '\172', '\105', '\040', '\163', '\172', '\040', '\061', + '\012', '\165', '\152', '\113', '\040', '\161', '\165', '\040', + '\061', '\012', '\161', '\142', '\155', '\040', '\161', '\165', + '\040', '\061', '\012', '\127', '\163', '\146', '\040', '\163', + '\164', '\040', '\061', '\012', '\155', '\121', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\163', '\121', '\163', + '\040', '\163', '\164', '\040', '\061', '\012', '\171', '\130', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\166', + '\131', '\145', '\040', '\145', '\162', '\040', '\061', '\012', + '\145', '\120', '\166', '\040', '\145', '\162', '\040', '\061', + '\012', '\141', '\103', '\166', '\040', '\141', '\156', '\040', + '\061', '\012', '\160', '\126', '\155', '\040', '\155', '\145', + '\040', '\061', '\012', '\172', '\170', '\117', '\040', '\163', + '\172', '\040', '\061', '\012', '\152', '\152', '\127', '\040', + '\151', '\152', '\040', '\061', '\012', '\166', '\147', '\111', + '\040', '\156', '\147', '\040', '\061', '\012', '\164', '\132', + '\143', '\040', '\164', '\150', '\040', '\061', '\012', '\121', + '\164', '\147', '\040', '\164', '\150', '\040', '\061', '\012', + '\166', '\115', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\124', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\115', '\170', '\152', '\040', '\151', '\152', + '\040', '\061', '\012', '\146', '\142', '\111', '\040', '\142', + '\145', '\040', '\061', '\012', '\161', '\101', '\165', '\040', + '\165', '\156', '\040', '\061', '\012', '\167', '\146', '\124', + '\040', '\167', '\141', '\040', '\061', '\012', '\146', '\143', + '\106', '\040', '\143', '\150', '\040', '\061', '\012', '\160', + '\146', '\113', '\040', '\160', '\162', '\040', '\061', '\012', + '\142', '\117', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\165', '\130', '\040', '\164', '\150', '\040', + '\061', '\012', '\143', '\112', '\155', '\040', '\143', '\150', + '\040', '\061', '\012', '\130', '\160', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\164', '\161', '\112', '\040', + '\164', '\150', '\040', '\061', '\012', '\117', '\166', '\146', + '\040', '\166', '\141', '\040', '\061', '\012', '\130', '\154', + '\152', '\040', '\154', '\145', '\040', '\061', '\012', '\116', + '\162', '\154', '\040', '\145', '\162', '\040', '\061', '\012', + '\146', '\170', '\127', '\040', '\146', '\157', '\040', '\061', + '\012', '\123', '\167', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\161', '\166', '\105', '\040', '\161', '\165', + '\040', '\061', '\012', '\161', '\160', '\131', '\040', '\161', + '\165', '\040', '\061', '\012', '\157', '\116', '\167', '\040', + '\157', '\156', '\040', '\061', '\012', '\153', '\131', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\152', '\130', + '\142', '\040', '\151', '\152', '\040', '\061', '\012', '\121', + '\146', '\153', '\040', '\153', '\141', '\040', '\061', '\012', + '\145', '\104', '\160', '\040', '\145', '\162', '\040', '\061', + '\012', '\126', '\161', '\142', '\040', '\161', '\165', '\040', + '\061', '\012', '\163', '\113', '\172', '\040', '\165', '\163', + '\040', '\061', '\012', '\161', '\152', '\160', '\040', '\161', + '\165', '\040', '\061', '\012', '\125', '\170', '\154', '\040', + '\154', '\145', '\040', '\061', '\012', '\114', '\153', '\171', + '\040', '\153', '\141', '\040', '\061', '\012', '\172', '\106', + '\171', '\040', '\163', '\172', '\040', '\061', '\012', '\156', + '\115', '\154', '\040', '\141', '\156', '\040', '\061', '\012', + '\171', '\131', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\143', '\121', '\145', '\040', '\143', '\150', '\040', + '\061', '\012', '\157', '\131', '\152', '\040', '\157', '\156', + '\040', '\061', '\012', '\164', '\142', '\102', '\040', '\164', + '\150', '\040', '\061', '\012', '\131', '\142', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\156', '\126', '\153', + '\040', '\156', '\144', '\040', '\061', '\012', '\142', '\130', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\114', + '\161', '\156', '\040', '\141', '\156', '\040', '\061', '\012', + '\155', '\144', '\113', '\040', '\144', '\145', '\040', '\061', + '\012', '\160', '\144', '\120', '\040', '\144', '\145', '\040', + '\061', '\012', '\164', '\161', '\123', '\040', '\164', '\150', + '\040', '\061', '\012', '\132', '\152', '\146', '\040', '\151', + '\152', '\040', '\061', '\012', '\153', '\143', '\103', '\040', + '\143', '\150', '\040', '\061', '\012', '\161', '\132', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\141', '\123', + '\144', '\040', '\141', '\156', '\040', '\061', '\012', '\103', + '\155', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\150', '\172', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\121', '\155', '\040', '\155', '\145', '\040', + '\061', '\012', '\107', '\161', '\147', '\040', '\161', '\165', + '\040', '\061', '\012', '\171', '\127', '\160', '\040', '\160', + '\162', '\040', '\061', '\012', '\130', '\162', '\167', '\040', + '\145', '\162', '\040', '\061', '\012', '\171', '\112', '\171', + '\040', '\156', '\171', '\040', '\061', '\012', '\163', '\161', + '\104', '\040', '\161', '\165', '\040', '\061', '\012', '\144', + '\127', '\142', '\040', '\144', '\145', '\040', '\061', '\012', + '\156', '\142', '\121', '\040', '\141', '\156', '\040', '\061', + '\012', '\151', '\167', '\120', '\040', '\151', '\156', '\040', + '\061', '\012', '\154', '\127', '\163', '\040', '\154', '\145', + '\040', '\061', '\012', '\124', '\163', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\144', '\110', '\172', '\040', + '\144', '\145', '\040', '\061', '\012', '\164', '\143', '\106', + '\040', '\164', '\150', '\040', '\061', '\012', '\121', '\153', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\102', + '\144', '\144', '\040', '\144', '\145', '\040', '\061', '\012', + '\115', '\170', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\152', '\126', '\040', '\151', '\152', '\040', + '\061', '\012', '\153', '\121', '\162', '\040', '\145', '\162', + '\040', '\061', '\012', '\144', '\156', '\111', '\040', '\141', + '\156', '\040', '\061', '\012', '\146', '\171', '\131', '\040', + '\156', '\171', '\040', '\061', '\012', '\141', '\106', '\161', + '\040', '\141', '\156', '\040', '\061', '\012', '\131', '\154', + '\170', '\040', '\154', '\145', '\040', '\061', '\012', '\131', + '\171', '\155', '\040', '\155', '\145', '\040', '\061', '\012', + '\152', '\142', '\126', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\143', '\126', '\040', '\143', '\150', '\040', + '\061', '\012', '\160', '\172', '\130', '\040', '\163', '\172', + '\040', '\061', '\012', '\161', '\122', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\144', '\152', '\101', '\040', + '\144', '\145', '\040', '\061', '\012', '\142', '\156', '\111', + '\040', '\141', '\156', '\040', '\061', '\012', '\114', '\154', + '\166', '\040', '\154', '\145', '\040', '\061', '\012', '\164', + '\155', '\132', '\040', '\164', '\150', '\040', '\061', '\012', + '\150', '\121', '\157', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\164', '\127', '\040', '\164', '\150', '\040', + '\061', '\012', '\122', '\170', '\172', '\040', '\163', '\172', + '\040', '\061', '\012', '\144', '\170', '\127', '\040', '\144', + '\145', '\040', '\061', '\012', '\161', '\164', '\127', '\040', + '\164', '\150', '\040', '\061', '\012', '\153', '\161', '\117', + '\040', '\161', '\165', '\040', '\061', '\012', '\154', '\110', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\154', + '\122', '\152', '\040', '\154', '\145', '\040', '\061', '\012', + '\150', '\116', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\107', '\151', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\143', '\131', '\161', '\040', '\143', '\150', + '\040', '\061', '\012', '\131', '\144', '\160', '\040', '\144', + '\145', '\040', '\061', '\012', '\161', '\127', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\170', '\153', '\102', + '\040', '\153', '\141', '\040', '\061', '\012', '\153', '\170', + '\103', '\040', '\153', '\141', '\040', '\061', '\012', '\154', + '\152', '\101', '\040', '\154', '\145', '\040', '\061', '\012', + '\121', '\167', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\155', '\103', '\160', '\040', '\155', '\145', '\040', + '\061', '\012', '\146', '\112', '\144', '\040', '\144', '\145', + '\040', '\061', '\012', '\166', '\103', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\126', '\143', '\172', '\040', + '\143', '\150', '\040', '\061', '\012', '\166', '\102', '\146', + '\040', '\166', '\141', '\040', '\061', '\012', '\143', '\131', + '\170', '\040', '\143', '\150', '\040', '\061', '\012', '\146', + '\110', '\167', '\040', '\167', '\141', '\040', '\061', '\012', + '\153', '\166', '\127', '\040', '\153', '\141', '\040', '\061', + '\012', '\112', '\155', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\150', '\121', '\152', '\040', '\164', '\150', + '\040', '\061', '\012', '\162', '\142', '\121', '\040', '\145', + '\162', '\040', '\061', '\012', '\166', '\170', '\130', '\040', + '\166', '\141', '\040', '\061', '\012', '\167', '\106', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\124', '\152', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\150', + '\170', '\122', '\040', '\164', '\150', '\040', '\061', '\012', + '\166', '\144', '\131', '\040', '\144', '\145', '\040', '\061', + '\012', '\160', '\155', '\106', '\040', '\155', '\145', '\040', + '\061', '\012', '\163', '\104', '\154', '\040', '\154', '\145', + '\040', '\061', '\012', '\162', '\126', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\167', '\104', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\147', '\102', '\167', + '\040', '\156', '\147', '\040', '\061', '\012', '\143', '\110', + '\146', '\040', '\143', '\150', '\040', '\061', '\012', '\160', + '\172', '\121', '\040', '\163', '\172', '\040', '\061', '\012', + '\154', '\126', '\160', '\040', '\154', '\145', '\040', '\061', + '\012', '\147', '\146', '\110', '\040', '\156', '\147', '\040', + '\061', '\012', '\157', '\107', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\164', '\166', '\112', '\040', '\164', + '\150', '\040', '\061', '\012', '\143', '\115', '\166', '\040', + '\143', '\150', '\040', '\061', '\012', '\170', '\156', '\123', + '\040', '\141', '\156', '\040', '\061', '\012', '\166', '\121', + '\170', '\040', '\166', '\141', '\040', '\061', '\012', '\165', + '\157', '\115', '\040', '\161', '\165', '\040', '\061', '\012', + '\172', '\153', '\130', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\110', '\160', '\040', '\163', '\172', '\040', + '\061', '\012', '\171', '\165', '\127', '\040', '\161', '\165', + '\040', '\061', '\012', '\121', '\142', '\166', '\040', '\166', + '\141', '\040', '\061', '\012', '\172', '\167', '\107', '\040', + '\163', '\172', '\040', '\061', '\012', '\143', '\160', '\130', + '\040', '\143', '\150', '\040', '\061', '\012', '\122', '\160', + '\166', '\040', '\166', '\141', '\040', '\061', '\012', '\172', + '\113', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\167', '\125', '\142', '\040', '\167', '\141', '\040', '\061', + '\012', '\161', '\156', '\112', '\040', '\141', '\156', '\040', + '\061', '\012', '\122', '\160', '\171', '\040', '\160', '\162', + '\040', '\061', '\012', '\142', '\143', '\123', '\040', '\143', + '\150', '\040', '\061', '\012', '\161', '\170', '\113', '\040', + '\161', '\165', '\040', '\061', '\012', '\161', '\152', '\104', + '\040', '\161', '\165', '\040', '\061', '\012', '\154', '\121', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\153', + '\162', '\130', '\040', '\145', '\162', '\040', '\061', '\012', + '\106', '\143', '\147', '\040', '\143', '\150', '\040', '\061', + '\012', '\157', '\126', '\170', '\040', '\157', '\156', '\040', + '\061', '\012', '\166', '\112', '\146', '\040', '\166', '\141', + '\040', '\061', '\012', '\102', '\166', '\153', '\040', '\153', + '\141', '\040', '\061', '\012', '\144', '\155', '\130', '\040', + '\144', '\145', '\040', '\061', '\012', '\127', '\144', '\152', + '\040', '\144', '\145', '\040', '\061', '\012', '\131', '\172', + '\160', '\040', '\163', '\172', '\040', '\061', '\012', '\131', + '\143', '\144', '\040', '\143', '\150', '\040', '\061', '\012', + '\152', '\113', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\153', '\162', '\110', '\040', '\145', '\162', '\040', + '\061', '\012', '\114', '\156', '\155', '\040', '\141', '\156', + '\040', '\061', '\012', '\172', '\103', '\155', '\040', '\163', + '\172', '\040', '\061', '\012', '\125', '\167', '\152', '\040', + '\151', '\152', '\040', '\061', '\012', '\125', '\166', '\153', + '\040', '\153', '\141', '\040', '\061', '\012', '\115', '\146', + '\152', '\040', '\151', '\152', '\040', '\061', '\012', '\171', + '\161', '\112', '\040', '\161', '\165', '\040', '\061', '\012', + '\114', '\146', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\110', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\153', '\147', '\112', '\040', '\156', '\147', + '\040', '\061', '\012', '\141', '\107', '\161', '\040', '\141', + '\156', '\040', '\061', '\012', '\164', '\152', '\110', '\040', + '\164', '\150', '\040', '\061', '\012', '\132', '\153', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\167', '\110', + '\166', '\040', '\166', '\141', '\040', '\061', '\012', '\116', + '\172', '\160', '\040', '\163', '\172', '\040', '\061', '\012', + '\143', '\132', '\170', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\166', '\113', '\040', '\151', '\152', '\040', + '\061', '\012', '\143', '\154', '\106', '\040', '\143', '\150', + '\040', '\061', '\012', '\170', '\155', '\104', '\040', '\155', + '\145', '\040', '\061', '\012', '\131', '\160', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\160', '\106', '\171', + '\040', '\160', '\162', '\040', '\061', '\012', '\150', '\166', + '\106', '\040', '\164', '\150', '\040', '\061', '\012', '\155', + '\164', '\127', '\040', '\164', '\150', '\040', '\061', '\012', + '\150', '\161', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\166', '\116', '\040', '\153', '\141', '\040', + '\061', '\012', '\164', '\143', '\132', '\040', '\164', '\150', + '\040', '\061', '\012', '\164', '\153', '\122', '\040', '\164', + '\150', '\040', '\061', '\012', '\160', '\144', '\110', '\040', + '\144', '\145', '\040', '\061', '\012', '\161', '\105', '\163', + '\040', '\161', '\165', '\040', '\061', '\012', '\132', '\143', + '\167', '\040', '\143', '\150', '\040', '\061', '\012', '\126', + '\167', '\165', '\040', '\165', '\156', '\040', '\061', '\012', + '\147', '\130', '\172', '\040', '\156', '\147', '\040', '\061', + '\012', '\155', '\127', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\155', '\127', '\166', '\040', '\166', '\141', + '\040', '\061', '\012', '\112', '\161', '\170', '\040', '\161', + '\165', '\040', '\061', '\012', '\157', '\123', '\152', '\040', + '\157', '\156', '\040', '\061', '\012', '\154', '\167', '\131', + '\040', '\154', '\145', '\040', '\061', '\012', '\124', '\153', + '\146', '\040', '\153', '\141', '\040', '\061', '\012', '\160', + '\143', '\103', '\040', '\143', '\150', '\040', '\061', '\012', + '\157', '\150', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\172', '\107', '\040', '\144', '\145', '\040', + '\061', '\012', '\146', '\144', '\116', '\040', '\144', '\145', + '\040', '\061', '\012', '\170', '\162', '\123', '\040', '\145', + '\162', '\040', '\061', '\012', '\150', '\110', '\153', '\040', + '\164', '\150', '\040', '\061', '\012', '\106', '\152', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\166', '\142', + '\132', '\040', '\166', '\141', '\040', '\061', '\012', '\125', + '\144', '\170', '\040', '\144', '\145', '\040', '\061', '\012', + '\167', '\172', '\130', '\040', '\163', '\172', '\040', '\061', + '\012', '\165', '\116', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\167', '\146', '\132', '\040', '\167', '\141', + '\040', '\061', '\012', '\163', '\167', '\102', '\040', '\163', + '\164', '\040', '\061', '\012', '\144', '\155', '\121', '\040', + '\144', '\145', '\040', '\061', '\012', '\144', '\143', '\101', + '\040', '\143', '\150', '\040', '\061', '\012', '\161', '\172', + '\120', '\040', '\161', '\165', '\040', '\061', '\012', '\152', + '\112', '\152', '\040', '\151', '\152', '\040', '\061', '\012', + '\161', '\127', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\126', '\153', '\040', '\164', '\150', '\040', + '\061', '\012', '\147', '\167', '\102', '\040', '\156', '\147', + '\040', '\061', '\012', '\142', '\111', '\167', '\040', '\167', + '\141', '\040', '\061', '\012', '\142', '\160', '\125', '\040', + '\160', '\162', '\040', '\061', '\012', '\142', '\167', '\115', + '\040', '\167', '\141', '\040', '\061', '\012', '\146', '\153', + '\101', '\040', '\153', '\141', '\040', '\061', '\012', '\170', + '\125', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\170', '\124', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\113', '\154', '\040', '\154', '\145', '\040', + '\061', '\012', '\154', '\170', '\123', '\040', '\154', '\145', + '\040', '\061', '\012', '\170', '\141', '\123', '\040', '\141', + '\156', '\040', '\061', '\012', '\171', '\166', '\121', '\040', + '\166', '\141', '\040', '\061', '\012', '\144', '\150', '\126', + '\040', '\164', '\150', '\040', '\061', '\012', '\155', '\144', + '\127', '\040', '\144', '\145', '\040', '\061', '\012', '\167', + '\146', '\112', '\040', '\167', '\141', '\040', '\061', '\012', + '\127', '\161', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\132', '\152', '\040', '\163', '\164', '\040', + '\061', '\012', '\114', '\170', '\171', '\040', '\156', '\171', + '\040', '\061', '\012', '\170', '\130', '\171', '\040', '\156', + '\171', '\040', '\061', '\012', '\161', '\104', '\155', '\040', + '\161', '\165', '\040', '\061', '\012', '\147', '\113', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\121', '\166', + '\152', '\040', '\151', '\152', '\040', '\061', '\012', '\153', + '\146', '\110', '\040', '\153', '\141', '\040', '\061', '\012', + '\141', '\121', '\160', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\106', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\156', '\152', '\127', '\040', '\141', '\156', + '\040', '\061', '\012', '\122', '\160', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\115', '\155', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\146', '\150', '\104', + '\040', '\164', '\150', '\040', '\061', '\012', '\152', '\113', + '\153', '\040', '\151', '\152', '\040', '\061', '\012', '\172', + '\101', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\161', '\146', '\114', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\167', '\116', '\040', '\167', '\141', '\040', + '\061', '\012', '\161', '\160', '\172', '\040', '\161', '\165', + '\040', '\061', '\012', '\150', '\170', '\120', '\040', '\164', + '\150', '\040', '\061', '\012', '\107', '\144', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\164', '\115', '\170', + '\040', '\164', '\150', '\040', '\061', '\012', '\152', '\167', + '\114', '\040', '\151', '\152', '\040', '\061', '\012', '\153', + '\102', '\142', '\040', '\153', '\141', '\040', '\061', '\012', + '\146', '\101', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\123', '\144', '\170', '\040', '\144', '\145', '\040', + '\061', '\012', '\112', '\155', '\166', '\040', '\166', '\141', + '\040', '\061', '\012', '\142', '\147', '\130', '\040', '\156', + '\147', '\040', '\061', '\012', '\170', '\127', '\160', '\040', + '\160', '\162', '\040', '\061', '\012', '\150', '\110', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\107', '\167', + '\167', '\040', '\167', '\141', '\040', '\061', '\012', '\106', + '\142', '\142', '\040', '\142', '\145', '\040', '\061', '\012', + '\172', '\157', '\124', '\040', '\157', '\156', '\040', '\061', + '\012', '\171', '\152', '\107', '\040', '\151', '\152', '\040', + '\061', '\012', '\122', '\154', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\166', '\106', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\172', '\143', '\113', '\040', + '\143', '\150', '\040', '\061', '\012', '\170', '\144', '\103', + '\040', '\144', '\145', '\040', '\061', '\012', '\167', '\166', + '\117', '\040', '\166', '\141', '\040', '\061', '\012', '\157', + '\121', '\154', '\040', '\154', '\145', '\040', '\061', '\012', + '\156', '\111', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\172', '\101', '\040', '\163', '\172', '\040', + '\061', '\012', '\122', '\172', '\152', '\040', '\163', '\172', + '\040', '\061', '\012', '\121', '\172', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\131', '\152', '\164', '\040', + '\164', '\150', '\040', '\061', '\012', '\170', '\153', '\121', + '\040', '\153', '\165', '\040', '\061', '\012', '\154', '\162', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\156', + '\167', '\132', '\040', '\141', '\156', '\040', '\061', '\012', + '\160', '\107', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\155', '\156', '\114', '\040', '\141', '\156', '\040', + '\061', '\012', '\122', '\154', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\143', '\143', '\104', '\040', '\143', + '\150', '\040', '\061', '\012', '\162', '\122', '\144', '\040', + '\145', '\162', '\040', '\061', '\012', '\117', '\146', '\152', + '\040', '\151', '\152', '\040', '\061', '\012', '\106', '\152', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\165', + '\165', '\117', '\040', '\161', '\165', '\040', '\061', '\012', + '\172', '\132', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\116', '\142', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\172', '\156', '\127', '\040', '\141', '\156', + '\040', '\061', '\012', '\152', '\142', '\110', '\040', '\151', + '\152', '\040', '\061', '\012', '\162', '\104', '\170', '\040', + '\145', '\162', '\040', '\061', '\012', '\121', '\155', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\144', '\167', + '\126', '\040', '\144', '\145', '\040', '\061', '\012', '\117', + '\161', '\166', '\040', '\161', '\165', '\040', '\061', '\012', + '\132', '\161', '\145', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\167', '\111', '\040', '\167', '\141', '\040', + '\061', '\012', '\156', '\152', '\120', '\040', '\141', '\156', + '\040', '\061', '\012', '\117', '\161', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\160', '\126', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\146', '\161', '\170', + '\040', '\161', '\165', '\040', '\061', '\012', '\147', '\146', + '\117', '\040', '\156', '\147', '\040', '\061', '\012', '\150', + '\161', '\125', '\040', '\164', '\150', '\040', '\061', '\012', + '\147', '\104', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\124', '\155', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\166', '\143', '\113', '\040', '\143', '\150', + '\040', '\061', '\012', '\161', '\155', '\126', '\040', '\161', + '\165', '\040', '\061', '\012', '\163', '\126', '\170', '\040', + '\163', '\164', '\040', '\061', '\012', '\127', '\146', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\155', '\112', + '\153', '\040', '\153', '\141', '\040', '\061', '\012', '\146', + '\165', '\113', '\040', '\161', '\165', '\040', '\061', '\012', + '\142', '\146', '\116', '\040', '\142', '\145', '\040', '\061', + '\012', '\161', '\146', '\124', '\040', '\161', '\165', '\040', + '\061', '\012', '\106', '\155', '\152', '\040', '\151', '\152', + '\040', '\061', '\012', '\164', '\142', '\116', '\040', '\164', + '\150', '\040', '\061', '\012', '\153', '\152', '\116', '\040', + '\151', '\152', '\040', '\061', '\012', '\171', '\150', '\132', + '\040', '\164', '\150', '\040', '\061', '\012', '\116', '\170', + '\153', '\040', '\153', '\141', '\040', '\061', '\012', '\167', + '\170', '\125', '\040', '\167', '\141', '\040', '\061', '\012', + '\172', '\130', '\142', '\040', '\163', '\172', '\040', '\061', + '\012', '\116', '\172', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\157', '\150', '\114', '\040', '\164', '\150', + '\040', '\061', '\012', '\160', '\126', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\132', '\163', '\170', '\040', + '\163', '\164', '\040', '\061', '\012', '\132', '\161', '\152', + '\040', '\161', '\165', '\040', '\061', '\012', '\167', '\125', + '\152', '\040', '\151', '\152', '\040', '\061', '\012', '\171', + '\152', '\103', '\040', '\151', '\152', '\040', '\061', '\012', + '\153', '\124', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\161', '\126', '\040', '\161', '\165', '\040', + '\061', '\012', '\106', '\171', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\111', '\143', '\144', '\040', '\143', + '\150', '\040', '\061', '\012', '\163', '\166', '\116', '\040', + '\163', '\164', '\040', '\061', '\012', '\112', '\152', '\166', + '\040', '\151', '\152', '\040', '\061', '\012', '\142', '\126', + '\160', '\040', '\160', '\162', '\040', '\061', '\012', '\146', + '\144', '\111', '\040', '\144', '\145', '\040', '\061', '\012', + '\156', '\142', '\130', '\040', '\141', '\156', '\040', '\061', + '\012', '\143', '\146', '\125', '\040', '\143', '\150', '\040', + '\061', '\012', '\154', '\107', '\155', '\040', '\154', '\145', + '\040', '\061', '\012', '\117', '\166', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\172', '\104', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\152', '\147', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\154', '\131', + '\162', '\040', '\145', '\162', '\040', '\061', '\012', '\150', + '\152', '\122', '\040', '\164', '\150', '\040', '\061', '\012', + '\161', '\120', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\151', '\122', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\132', '\162', '\170', '\040', '\145', '\162', + '\040', '\061', '\012', '\167', '\160', '\124', '\040', '\160', + '\162', '\040', '\061', '\012', '\170', '\163', '\102', '\040', + '\163', '\164', '\040', '\061', '\012', '\161', '\170', '\124', + '\040', '\161', '\165', '\040', '\061', '\012', '\147', '\106', + '\170', '\040', '\156', '\147', '\040', '\061', '\012', '\161', + '\157', '\112', '\040', '\161', '\165', '\040', '\061', '\012', + '\163', '\155', '\104', '\040', '\163', '\164', '\040', '\061', + '\012', '\154', '\142', '\115', '\040', '\154', '\145', '\040', + '\061', '\012', '\167', '\103', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\167', '\106', '\155', '\040', '\155', + '\145', '\040', '\061', '\012', '\130', '\154', '\166', '\040', + '\154', '\145', '\040', '\061', '\012', '\172', '\171', '\125', + '\040', '\163', '\172', '\040', '\061', '\012', '\166', '\106', + '\153', '\040', '\153', '\141', '\040', '\061', '\012', '\164', + '\152', '\122', '\040', '\164', '\150', '\040', '\061', '\012', + '\151', '\131', '\170', '\040', '\151', '\156', '\040', '\061', + '\012', '\165', '\112', '\153', '\040', '\161', '\165', '\040', + '\061', '\012', '\121', '\145', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\130', '\162', '\166', '\040', '\145', + '\162', '\040', '\061', '\012', '\102', '\161', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\126', '\144', '\142', + '\040', '\144', '\145', '\040', '\061', '\012', '\172', '\156', + '\122', '\040', '\141', '\156', '\040', '\061', '\012', '\160', + '\155', '\114', '\040', '\155', '\145', '\040', '\061', '\012', + '\164', '\166', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\124', '\155', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\104', '\147', '\142', '\040', '\156', '\147', + '\040', '\061', '\012', '\157', '\172', '\117', '\040', '\157', + '\156', '\040', '\061', '\012', '\146', '\121', '\142', '\040', + '\142', '\145', '\040', '\061', '\012', '\120', '\161', '\142', + '\040', '\161', '\165', '\040', '\061', '\012', '\161', '\131', + '\156', '\040', '\141', '\156', '\040', '\061', '\012', '\170', + '\120', '\155', '\040', '\155', '\145', '\040', '\061', '\012', + '\147', '\127', '\146', '\040', '\156', '\147', '\040', '\061', + '\012', '\143', '\103', '\166', '\040', '\143', '\150', '\040', + '\061', '\012', '\161', '\145', '\120', '\040', '\161', '\165', + '\040', '\061', '\012', '\161', '\132', '\155', '\040', '\161', + '\165', '\040', '\061', '\012', '\144', '\147', '\132', '\040', + '\156', '\147', '\040', '\061', '\012', '\155', '\152', '\117', + '\040', '\151', '\152', '\040', '\061', '\012', '\147', '\103', + '\167', '\040', '\156', '\147', '\040', '\061', '\012', '\163', + '\166', '\121', '\040', '\163', '\164', '\040', '\061', '\012', + '\122', '\161', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\142', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\114', '\153', '\152', '\040', '\151', '\152', + '\040', '\061', '\012', '\106', '\172', '\141', '\040', '\141', + '\156', '\040', '\061', '\012', '\152', '\154', '\102', '\040', + '\154', '\145', '\040', '\061', '\012', '\151', '\127', '\152', + '\040', '\151', '\156', '\040', '\061', '\012', '\132', '\170', + '\151', '\040', '\151', '\156', '\040', '\061', '\012', '\113', + '\170', '\167', '\040', '\167', '\141', '\040', '\061', '\012', + '\152', '\143', '\112', '\040', '\151', '\152', '\040', '\061', + '\012', '\165', '\103', '\146', '\040', '\161', '\165', '\040', + '\061', '\012', '\143', '\101', '\170', '\040', '\143', '\150', + '\040', '\061', '\012', '\126', '\152', '\167', '\040', '\151', + '\152', '\040', '\061', '\012', '\166', '\125', '\163', '\040', + '\163', '\164', '\040', '\061', '\012', '\115', '\156', '\161', + '\040', '\141', '\156', '\040', '\061', '\012', '\152', '\152', + '\115', '\040', '\151', '\152', '\040', '\061', '\012', '\166', + '\125', '\170', '\040', '\166', '\141', '\040', '\061', '\012', + '\165', '\132', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\167', '\125', '\040', '\164', '\150', '\040', + '\061', '\012', '\131', '\164', '\166', '\040', '\164', '\150', + '\040', '\061', '\012', '\150', '\122', '\160', '\040', '\164', + '\150', '\040', '\061', '\012', '\153', '\172', '\126', '\040', + '\163', '\172', '\040', '\061', '\012', '\155', '\166', '\131', + '\040', '\166', '\141', '\040', '\061', '\012', '\152', '\106', + '\152', '\040', '\151', '\152', '\040', '\061', '\012', '\152', + '\102', '\160', '\040', '\151', '\152', '\040', '\061', '\012', + '\153', '\107', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\161', '\125', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\161', '\147', '\122', '\040', '\161', '\165', + '\040', '\061', '\012', '\154', '\127', '\142', '\040', '\154', + '\145', '\040', '\061', '\012', '\167', '\167', '\120', '\040', + '\167', '\141', '\040', '\061', '\012', '\167', '\166', '\105', + '\040', '\166', '\141', '\040', '\061', '\012', '\106', '\163', + '\170', '\040', '\163', '\164', '\040', '\061', '\012', '\111', + '\172', '\170', '\040', '\163', '\172', '\040', '\061', '\012', + '\142', '\167', '\103', '\040', '\167', '\141', '\040', '\061', + '\012', '\106', '\155', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\143', '\114', '\144', '\040', '\143', '\150', + '\040', '\061', '\012', '\142', '\122', '\154', '\040', '\154', + '\145', '\040', '\061', '\012', '\151', '\130', '\146', '\040', + '\151', '\156', '\040', '\061', '\012', '\171', '\115', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\143', '\161', + '\120', '\040', '\143', '\150', '\040', '\061', '\012', '\152', + '\163', '\114', '\040', '\163', '\164', '\040', '\061', '\012', + '\152', '\111', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\165', '\107', '\040', '\161', '\165', '\040', + '\061', '\012', '\114', '\142', '\166', '\040', '\166', '\141', + '\040', '\061', '\012', '\105', '\161', '\146', '\040', '\161', + '\165', '\040', '\061', '\012', '\117', '\147', '\146', '\040', + '\156', '\147', '\040', '\061', '\012', '\153', '\107', '\166', + '\040', '\153', '\141', '\040', '\061', '\012', '\160', '\152', + '\113', '\040', '\151', '\152', '\040', '\061', '\012', '\166', + '\143', '\121', '\040', '\143', '\150', '\040', '\061', '\012', + '\130', '\172', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\125', '\166', '\040', '\151', '\152', '\040', + '\061', '\012', '\167', '\107', '\144', '\040', '\144', '\145', + '\040', '\061', '\012', '\150', '\155', '\130', '\040', '\164', + '\150', '\040', '\061', '\012', '\171', '\161', '\155', '\040', + '\161', '\165', '\040', '\061', '\012', '\161', '\153', '\105', + '\040', '\161', '\165', '\040', '\061', '\012', '\172', '\147', + '\130', '\040', '\156', '\147', '\040', '\061', '\012', '\166', + '\167', '\117', '\040', '\166', '\141', '\040', '\061', '\012', + '\167', '\155', '\123', '\040', '\155', '\145', '\040', '\061', + '\012', '\166', '\150', '\124', '\040', '\164', '\150', '\040', + '\061', '\012', '\163', '\171', '\130', '\040', '\163', '\164', + '\040', '\061', '\012', '\156', '\142', '\103', '\040', '\141', + '\156', '\040', '\061', '\012', '\172', '\147', '\127', '\040', + '\156', '\147', '\040', '\061', '\012', '\166', '\161', '\115', + '\040', '\161', '\165', '\040', '\061', '\012', '\144', '\127', + '\146', '\040', '\144', '\145', '\040', '\061', '\012', '\143', + '\167', '\106', '\040', '\143', '\150', '\040', '\061', '\012', + '\144', '\156', '\106', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\104', '\151', '\040', '\161', '\165', '\040', + '\061', '\012', '\161', '\123', '\167', '\040', '\161', '\165', + '\040', '\061', '\012', '\152', '\121', '\146', '\040', '\151', + '\152', '\040', '\061', '\012', '\143', '\162', '\132', '\040', + '\143', '\150', '\040', '\061', '\012', '\161', '\107', '\154', + '\040', '\161', '\165', '\040', '\061', '\012', '\127', '\170', + '\165', '\040', '\161', '\165', '\040', '\061', '\012', '\147', + '\162', '\127', '\040', '\156', '\147', '\040', '\061', '\012', + '\147', '\154', '\130', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\106', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\160', '\142', '\106', '\040', '\160', '\162', + '\040', '\061', '\012', '\142', '\116', '\146', '\040', '\142', + '\145', '\040', '\061', '\012', '\121', '\143', '\146', '\040', + '\143', '\150', '\040', '\061', '\012', '\146', '\126', '\170', + '\040', '\146', '\157', '\040', '\061', '\012', '\160', '\120', + '\146', '\040', '\160', '\162', '\040', '\061', '\012', '\160', + '\126', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\170', '\154', '\107', '\040', '\154', '\145', '\040', '\061', + '\012', '\104', '\167', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\170', '\121', '\152', '\040', '\151', '\152', + '\040', '\061', '\012', '\154', '\153', '\121', '\040', '\154', + '\145', '\040', '\061', '\012', '\163', '\161', '\110', '\040', + '\161', '\165', '\040', '\061', '\012', '\131', '\171', '\170', + '\040', '\156', '\171', '\040', '\061', '\012', '\166', '\106', + '\155', '\040', '\166', '\141', '\040', '\061', '\012', '\164', + '\121', '\157', '\040', '\164', '\150', '\040', '\061', '\012', + '\172', '\154', '\125', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\154', '\127', '\040', '\154', '\145', '\040', + '\061', '\012', '\147', '\154', '\127', '\040', '\156', '\147', + '\040', '\061', '\012', '\161', '\155', '\127', '\040', '\161', + '\165', '\040', '\061', '\012', '\141', '\127', '\154', '\040', + '\141', '\156', '\040', '\061', '\012', '\172', '\155', '\126', + '\040', '\163', '\172', '\040', '\061', '\012', '\147', '\114', + '\155', '\040', '\156', '\147', '\040', '\061', '\012', '\147', + '\154', '\102', '\040', '\156', '\147', '\040', '\061', '\012', + '\164', '\161', '\101', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\147', '\112', '\040', '\164', '\150', '\040', + '\061', '\012', '\143', '\107', '\142', '\040', '\143', '\150', + '\040', '\061', '\012', '\161', '\167', '\105', '\040', '\161', + '\165', '\040', '\061', '\012', '\106', '\146', '\171', '\040', + '\156', '\171', '\040', '\061', '\012', '\167', '\155', '\114', + '\040', '\155', '\145', '\040', '\061', '\012', '\170', '\114', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\163', + '\142', '\105', '\040', '\163', '\164', '\040', '\061', '\012', + '\142', '\121', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\153', '\122', '\040', '\153', '\141', '\040', + '\061', '\012', '\171', '\106', '\144', '\040', '\144', '\145', + '\040', '\061', '\012', '\117', '\155', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\130', '\146', '\152', '\040', + '\151', '\152', '\040', '\061', '\012', '\167', '\112', '\152', + '\040', '\151', '\152', '\040', '\061', '\012', '\114', '\167', + '\163', '\040', '\163', '\164', '\040', '\061', '\012', '\167', + '\146', '\125', '\040', '\167', '\141', '\040', '\061', '\012', + '\172', '\146', '\153', '\040', '\163', '\172', '\040', '\061', + '\012', '\154', '\116', '\166', '\040', '\154', '\145', '\040', + '\061', '\012', '\171', '\153', '\121', '\040', '\153', '\141', + '\040', '\061', '\012', '\170', '\104', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\152', '\104', '\167', '\040', + '\151', '\152', '\040', '\061', '\012', '\172', '\142', '\170', + '\040', '\163', '\172', '\040', '\061', '\012', '\166', '\121', + '\163', '\040', '\163', '\164', '\040', '\061', '\012', '\166', + '\166', '\115', '\040', '\166', '\141', '\040', '\061', '\012', + '\130', '\161', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\114', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\172', '\153', '\132', '\040', '\163', '\172', + '\040', '\061', '\012', '\161', '\101', '\147', '\040', '\161', + '\165', '\040', '\061', '\012', '\130', '\152', '\167', '\040', + '\151', '\152', '\040', '\061', '\012', '\143', '\106', '\167', + '\040', '\143', '\150', '\040', '\061', '\012', '\162', '\167', + '\121', '\040', '\145', '\162', '\040', '\061', '\012', '\155', + '\127', '\153', '\040', '\153', '\141', '\040', '\061', '\012', + '\131', '\162', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\145', '\125', '\157', '\040', '\145', '\162', '\040', + '\061', '\012', '\165', '\104', '\155', '\040', '\161', '\165', + '\040', '\061', '\012', '\115', '\150', '\167', '\040', '\164', + '\150', '\040', '\061', '\012', '\146', '\107', '\160', '\040', + '\160', '\162', '\040', '\061', '\012', '\122', '\160', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\163', '\142', + '\106', '\040', '\163', '\164', '\040', '\061', '\012', '\156', + '\146', '\130', '\040', '\141', '\156', '\040', '\061', '\012', + '\127', '\146', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\115', '\167', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\161', '\104', '\152', '\040', '\161', '\165', + '\040', '\061', '\012', '\127', '\160', '\167', '\040', '\160', + '\162', '\040', '\061', '\012', '\172', '\106', '\166', '\040', + '\163', '\172', '\040', '\061', '\012', '\161', '\130', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\161', '\163', + '\124', '\040', '\161', '\165', '\040', '\061', '\012', '\160', + '\132', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\154', '\114', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\161', '\102', '\040', '\161', '\165', '\040', + '\061', '\012', '\130', '\152', '\157', '\040', '\157', '\156', + '\040', '\061', '\012', '\153', '\104', '\153', '\040', '\153', + '\141', '\040', '\061', '\012', '\112', '\170', '\146', '\040', + '\146', '\157', '\040', '\061', '\012', '\126', '\161', '\172', + '\040', '\161', '\165', '\040', '\061', '\012', '\110', '\166', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\132', + '\161', '\167', '\040', '\161', '\165', '\040', '\061', '\012', + '\153', '\122', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\164', '\166', '\122', '\040', '\164', '\150', '\040', + '\061', '\012', '\144', '\116', '\170', '\040', '\144', '\145', + '\040', '\061', '\012', '\152', '\127', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\156', '\122', '\167', '\040', + '\141', '\156', '\040', '\061', '\012', '\162', '\107', '\142', + '\040', '\145', '\162', '\040', '\061', '\012', '\166', '\132', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\130', + '\164', '\172', '\040', '\164', '\150', '\040', '\061', '\012', + '\153', '\132', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\126', '\155', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\144', '\115', '\160', '\040', '\144', '\145', + '\040', '\061', '\012', '\143', '\120', '\171', '\040', '\143', + '\150', '\040', '\061', '\012', '\165', '\172', '\122', '\040', + '\161', '\165', '\040', '\061', '\012', '\171', '\152', '\105', + '\040', '\151', '\152', '\040', '\061', '\012', '\147', '\172', + '\106', '\040', '\156', '\147', '\040', '\061', '\012', '\164', + '\103', '\160', '\040', '\164', '\150', '\040', '\061', '\012', + '\161', '\146', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\143', '\161', '\040', '\143', '\150', '\040', + '\061', '\012', '\132', '\146', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\153', '\167', '\103', '\040', '\153', + '\141', '\040', '\061', '\012', '\146', '\153', '\115', '\040', + '\153', '\157', '\040', '\061', '\012', '\166', '\112', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\145', '\103', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\167', + '\120', '\160', '\040', '\160', '\162', '\040', '\061', '\012', + '\161', '\112', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\155', '\131', '\040', '\144', '\145', '\040', + '\061', '\012', '\165', '\115', '\152', '\040', '\161', '\165', + '\040', '\061', '\012', '\146', '\113', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\163', '\161', '\125', '\040', + '\161', '\165', '\040', '\061', '\012', '\166', '\116', '\160', + '\040', '\166', '\141', '\040', '\061', '\012', '\103', '\162', + '\152', '\040', '\145', '\162', '\040', '\061', '\012', '\150', + '\163', '\110', '\040', '\164', '\150', '\040', '\061', '\012', + '\126', '\167', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\123', '\144', '\171', '\040', '\144', '\145', '\040', + '\061', '\012', '\106', '\160', '\167', '\040', '\160', '\162', + '\040', '\061', '\012', '\127', '\143', '\161', '\040', '\143', + '\150', '\040', '\061', '\012', '\160', '\152', '\127', '\040', + '\151', '\152', '\040', '\061', '\012', '\144', '\167', '\127', + '\040', '\144', '\145', '\040', '\061', '\012', '\147', '\152', + '\130', '\040', '\156', '\147', '\040', '\061', '\012', '\171', + '\132', '\153', '\040', '\153', '\141', '\040', '\061', '\012', + '\143', '\113', '\147', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\144', '\122', '\040', '\144', '\145', '\040', + '\061', '\012', '\167', '\161', '\127', '\040', '\161', '\165', + '\040', '\061', '\012', '\153', '\150', '\104', '\040', '\164', + '\150', '\040', '\061', '\012', '\166', '\147', '\107', '\040', + '\156', '\147', '\040', '\061', '\012', '\166', '\115', '\154', + '\040', '\154', '\145', '\040', '\061', '\012', '\161', '\156', + '\121', '\040', '\141', '\156', '\040', '\061', '\012', '\150', + '\112', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\146', '\166', '\103', '\040', '\166', '\141', '\040', '\061', + '\012', '\143', '\160', '\122', '\040', '\143', '\150', '\040', + '\061', '\012', '\127', '\164', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\165', '\171', '\130', '\040', '\161', + '\165', '\040', '\061', '\012', '\143', '\130', '\146', '\040', + '\143', '\150', '\040', '\061', '\012', '\165', '\113', '\166', + '\040', '\161', '\165', '\040', '\061', '\012', '\147', '\126', + '\166', '\040', '\156', '\147', '\040', '\061', '\012', '\170', + '\172', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\143', '\120', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\124', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\163', '\106', '\152', '\040', '\163', '\164', + '\040', '\061', '\012', '\155', '\172', '\130', '\040', '\163', + '\172', '\040', '\061', '\012', '\147', '\115', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\162', '\170', '\111', + '\040', '\145', '\162', '\040', '\061', '\012', '\145', '\131', + '\146', '\040', '\145', '\162', '\040', '\061', '\012', '\153', + '\167', '\102', '\040', '\153', '\141', '\040', '\061', '\012', + '\145', '\121', '\153', '\040', '\145', '\162', '\040', '\061', + '\012', '\152', '\102', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\154', '\142', '\110', '\040', '\154', '\145', + '\040', '\061', '\012', '\161', '\103', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\127', '\156', '\166', '\040', + '\141', '\156', '\040', '\061', '\012', '\147', '\131', '\144', + '\040', '\156', '\147', '\040', '\061', '\012', '\132', '\170', + '\145', '\040', '\145', '\162', '\040', '\061', '\012', '\146', + '\132', '\152', '\040', '\151', '\152', '\040', '\061', '\012', + '\110', '\147', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\142', '\122', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\146', '\160', '\122', '\040', '\160', '\162', + '\040', '\061', '\012', '\143', '\142', '\122', '\040', '\143', + '\150', '\040', '\061', '\012', '\154', '\161', '\124', '\040', + '\161', '\165', '\040', '\061', '\012', '\143', '\115', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\164', '\121', + '\171', '\040', '\164', '\157', '\040', '\061', '\012', '\166', + '\170', '\107', '\040', '\166', '\141', '\040', '\061', '\012', + '\147', '\160', '\102', '\040', '\156', '\147', '\040', '\061', + '\012', '\107', '\153', '\167', '\040', '\153', '\141', '\040', + '\061', '\012', '\172', '\161', '\130', '\040', '\161', '\165', + '\040', '\061', '\012', '\164', '\120', '\167', '\040', '\164', + '\150', '\040', '\061', '\012', '\146', '\156', '\116', '\040', + '\141', '\156', '\040', '\061', '\012', '\107', '\153', '\160', + '\040', '\153', '\141', '\040', '\061', '\012', '\155', '\166', + '\121', '\040', '\166', '\141', '\040', '\061', '\012', '\150', + '\110', '\146', '\040', '\164', '\150', '\040', '\061', '\012', + '\167', '\146', '\123', '\040', '\167', '\141', '\040', '\061', + '\012', '\161', '\103', '\170', '\040', '\161', '\165', '\040', + '\061', '\012', '\155', '\161', '\110', '\040', '\161', '\165', + '\040', '\061', '\012', '\150', '\147', '\122', '\040', '\164', + '\150', '\040', '\061', '\012', '\115', '\167', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\142', '\161', '\121', + '\040', '\161', '\165', '\040', '\061', '\012', '\106', '\153', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\157', + '\106', '\166', '\040', '\157', '\156', '\040', '\061', '\012', + '\104', '\144', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\111', '\157', '\040', '\161', '\165', '\040', + '\061', '\012', '\131', '\146', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\171', '\147', '\121', '\040', '\156', + '\147', '\040', '\061', '\012', '\146', '\170', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\132', '\161', '\144', + '\040', '\161', '\165', '\040', '\061', '\012', '\110', '\164', + '\156', '\040', '\164', '\150', '\040', '\061', '\012', '\107', + '\166', '\172', '\040', '\163', '\172', '\040', '\061', '\012', + '\172', '\122', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\103', '\142', '\040', '\166', '\141', '\040', + '\061', '\012', '\162', '\152', '\124', '\040', '\162', '\157', + '\040', '\061', '\012', '\162', '\152', '\104', '\040', '\145', + '\162', '\040', '\061', '\012', '\121', '\160', '\155', '\040', + '\155', '\145', '\040', '\061', '\012', '\130', '\144', '\142', + '\040', '\144', '\145', '\040', '\061', '\012', '\114', '\153', + '\146', '\040', '\153', '\141', '\040', '\061', '\012', '\101', + '\152', '\170', '\040', '\151', '\152', '\040', '\061', '\012', + '\131', '\154', '\172', '\040', '\154', '\145', '\040', '\061', + '\012', '\121', '\164', '\142', '\040', '\164', '\150', '\040', + '\061', '\012', '\142', '\110', '\172', '\040', '\163', '\172', + '\040', '\061', '\012', '\142', '\104', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\114', '\161', '\170', '\040', + '\161', '\165', '\040', '\061', '\012', '\171', '\150', '\127', + '\040', '\164', '\150', '\040', '\061', '\012', '\172', '\114', + '\166', '\040', '\163', '\172', '\040', '\061', '\012', '\170', + '\147', '\113', '\040', '\156', '\147', '\040', '\061', '\012', + '\145', '\127', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\152', '\123', '\040', '\163', '\164', '\040', + '\061', '\012', '\161', '\126', '\145', '\040', '\161', '\165', + '\040', '\061', '\012', '\117', '\153', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\105', '\167', '\152', '\040', + '\151', '\152', '\040', '\061', '\012', '\104', '\163', '\166', + '\040', '\163', '\164', '\040', '\061', '\012', '\152', '\150', + '\111', '\040', '\164', '\150', '\040', '\061', '\012', '\170', + '\107', '\146', '\040', '\146', '\157', '\040', '\061', '\012', + '\117', '\153', '\170', '\040', '\153', '\141', '\040', '\061', + '\012', '\106', '\161', '\170', '\040', '\161', '\165', '\040', + '\061', '\012', '\144', '\120', '\166', '\040', '\144', '\145', + '\040', '\061', '\012', '\172', '\163', '\113', '\040', '\163', + '\164', '\040', '\061', '\012', '\161', '\114', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\146', '\153', '\102', + '\040', '\153', '\141', '\040', '\061', '\012', '\143', '\103', + '\142', '\040', '\143', '\150', '\040', '\061', '\012', '\147', + '\116', '\160', '\040', '\156', '\147', '\040', '\061', '\012', + '\121', '\167', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\172', '\124', '\146', '\040', '\163', '\172', '\040', + '\061', '\012', '\120', '\161', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\162', '\106', '\166', '\040', '\162', + '\157', '\040', '\061', '\012', '\122', '\167', '\164', '\040', + '\164', '\150', '\040', '\061', '\012', '\165', '\113', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\150', '\161', + '\116', '\040', '\164', '\150', '\040', '\061', '\012', '\153', + '\155', '\113', '\040', '\153', '\141', '\040', '\061', '\012', + '\167', '\165', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\156', '\132', '\040', '\141', '\156', '\040', + '\061', '\012', '\164', '\147', '\115', '\040', '\164', '\150', + '\040', '\061', '\012', '\121', '\144', '\163', '\040', '\163', + '\164', '\040', '\061', '\012', '\101', '\170', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\170', '\167', '\117', + '\040', '\167', '\141', '\040', '\061', '\012', '\145', '\121', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\155', + '\106', '\152', '\040', '\151', '\152', '\040', '\061', '\012', + '\104', '\160', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\160', '\121', '\155', '\040', '\155', '\145', '\040', + '\061', '\012', '\141', '\106', '\160', '\040', '\141', '\156', + '\040', '\061', '\012', '\155', '\146', '\102', '\040', '\155', + '\145', '\040', '\061', '\012', '\146', '\160', '\101', '\040', + '\160', '\162', '\040', '\061', '\012', '\152', '\147', '\132', + '\040', '\156', '\147', '\040', '\061', '\012', '\154', '\107', + '\153', '\040', '\154', '\145', '\040', '\061', '\012', '\170', + '\143', '\101', '\040', '\143', '\150', '\040', '\061', '\012', + '\147', '\127', '\167', '\040', '\156', '\147', '\040', '\061', + '\012', '\154', '\172', '\106', '\040', '\154', '\145', '\040', + '\061', '\012', '\170', '\163', '\121', '\040', '\163', '\164', + '\040', '\061', '\012', '\142', '\121', '\170', '\040', '\142', + '\145', '\040', '\061', '\012', '\167', '\152', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\142', '\104', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\127', '\160', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\162', + '\146', '\126', '\040', '\145', '\162', '\040', '\061', '\012', + '\132', '\142', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\150', '\113', '\161', '\040', '\164', '\150', '\040', + '\061', '\012', '\161', '\130', '\141', '\040', '\141', '\162', + '\040', '\061', '\012', '\167', '\152', '\101', '\040', '\151', + '\152', '\040', '\061', '\012', '\166', '\172', '\123', '\040', + '\163', '\172', '\040', '\061', '\012', '\143', '\127', '\171', + '\040', '\143', '\150', '\040', '\061', '\012', '\147', '\152', + '\113', '\040', '\156', '\147', '\040', '\061', '\012', '\171', + '\122', '\142', '\040', '\142', '\145', '\040', '\061', '\012', + '\161', '\147', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\161', '\106', '\040', '\161', '\165', '\040', + '\061', '\012', '\161', '\156', '\125', '\040', '\141', '\156', + '\040', '\061', '\012', '\132', '\161', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\130', '\161', '\147', '\040', + '\161', '\165', '\040', '\061', '\012', '\172', '\114', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\147', '\172', + '\126', '\040', '\156', '\147', '\040', '\061', '\012', '\113', + '\161', '\163', '\040', '\161', '\165', '\040', '\061', '\012', + '\172', '\147', '\132', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\161', '\107', '\040', '\161', '\165', '\040', + '\061', '\012', '\160', '\161', '\112', '\040', '\161', '\165', + '\040', '\061', '\012', '\111', '\145', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\150', '\152', '\110', '\040', + '\164', '\150', '\040', '\061', '\012', '\166', '\155', '\116', + '\040', '\166', '\141', '\040', '\061', '\012', '\151', '\165', + '\106', '\040', '\161', '\165', '\040', '\061', '\012', '\167', + '\107', '\171', '\040', '\167', '\141', '\040', '\061', '\012', + '\113', '\144', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\121', '\142', '\040', '\164', '\150', '\040', + '\061', '\012', '\152', '\127', '\162', '\040', '\145', '\162', + '\040', '\061', '\012', '\103', '\170', '\171', '\040', '\156', + '\171', '\040', '\061', '\012', '\113', '\161', '\172', '\040', + '\161', '\165', '\040', '\061', '\012', '\167', '\130', '\162', + '\040', '\145', '\162', '\040', '\061', '\012', '\170', '\157', + '\121', '\040', '\157', '\156', '\040', '\061', '\012', '\167', + '\102', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\161', '\171', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\150', '\103', '\040', '\164', '\150', '\040', + '\061', '\012', '\126', '\160', '\171', '\040', '\160', '\162', + '\040', '\061', '\012', '\156', '\112', '\142', '\040', '\141', + '\156', '\040', '\061', '\012', '\165', '\107', '\167', '\040', + '\161', '\165', '\040', '\061', '\012', '\150', '\150', '\130', + '\040', '\164', '\150', '\040', '\061', '\012', '\155', '\152', + '\123', '\040', '\151', '\152', '\040', '\061', '\012', '\123', + '\143', '\166', '\040', '\143', '\150', '\040', '\061', '\012', + '\150', '\106', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\113', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\130', '\155', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\142', '\144', '\124', '\040', '\144', + '\145', '\040', '\061', '\012', '\163', '\112', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\170', '\124', '\155', + '\040', '\155', '\145', '\040', '\061', '\012', '\161', '\152', + '\172', '\040', '\161', '\165', '\040', '\061', '\012', '\115', + '\161', '\160', '\040', '\161', '\165', '\040', '\061', '\012', + '\144', '\110', '\160', '\040', '\144', '\145', '\040', '\061', + '\012', '\162', '\122', '\156', '\040', '\141', '\162', '\040', + '\061', '\012', '\130', '\154', '\146', '\040', '\154', '\145', + '\040', '\061', '\012', '\143', '\116', '\163', '\040', '\143', + '\150', '\040', '\061', '\012', '\130', '\161', '\154', '\040', + '\161', '\165', '\040', '\061', '\012', '\151', '\106', '\172', + '\040', '\151', '\156', '\040', '\061', '\012', '\116', '\154', + '\153', '\040', '\154', '\145', '\040', '\061', '\012', '\163', + '\120', '\167', '\040', '\163', '\164', '\040', '\061', '\012', + '\166', '\127', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\130', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\106', '\156', '\161', '\040', '\141', '\156', + '\040', '\061', '\012', '\157', '\172', '\112', '\040', '\157', + '\156', '\040', '\061', '\012', '\172', '\111', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\154', '\123', '\146', + '\040', '\154', '\145', '\040', '\061', '\012', '\167', '\122', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\102', + '\166', '\160', '\040', '\166', '\141', '\040', '\061', '\012', + '\127', '\167', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\160', '\127', '\147', '\040', '\160', '\162', '\040', + '\061', '\012', '\160', '\114', '\153', '\040', '\153', '\141', + '\040', '\061', '\012', '\153', '\162', '\112', '\040', '\145', + '\162', '\040', '\061', '\012', '\132', '\146', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\171', '\111', '\170', + '\040', '\156', '\171', '\040', '\061', '\012', '\157', '\113', + '\170', '\040', '\157', '\156', '\040', '\061', '\012', '\161', + '\114', '\142', '\040', '\161', '\165', '\040', '\061', '\012', + '\144', '\110', '\152', '\040', '\144', '\145', '\040', '\061', + '\012', '\157', '\161', '\113', '\040', '\161', '\165', '\040', + '\061', '\012', '\143', '\170', '\103', '\040', '\143', '\150', + '\040', '\061', '\012', '\167', '\112', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\167', '\132', '\144', '\040', + '\144', '\145', '\040', '\061', '\012', '\143', '\127', '\172', + '\040', '\143', '\150', '\040', '\061', '\012', '\171', '\161', + '\123', '\040', '\161', '\165', '\040', '\061', '\012', '\153', + '\130', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\146', '\131', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\144', '\107', '\171', '\040', '\144', '\145', '\040', + '\061', '\012', '\144', '\104', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\160', '\113', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\130', '\152', '\144', '\040', + '\144', '\145', '\040', '\061', '\012', '\163', '\152', '\115', + '\040', '\163', '\164', '\040', '\061', '\012', '\163', '\146', + '\103', '\040', '\163', '\164', '\040', '\061', '\012', '\144', + '\115', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\144', '\132', '\160', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\143', '\104', '\040', '\143', '\150', '\040', + '\061', '\012', '\121', '\157', '\152', '\040', '\157', '\156', + '\040', '\061', '\012', '\147', '\170', '\103', '\040', '\156', + '\147', '\040', '\061', '\012', '\132', '\146', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\150', '\131', '\166', + '\040', '\164', '\150', '\040', '\061', '\012', '\170', '\127', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\147', + '\132', '\167', '\040', '\156', '\147', '\040', '\061', '\012', + '\160', '\121', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\130', '\154', '\142', '\040', '\154', '\145', '\040', + '\061', '\012', '\147', '\121', '\172', '\040', '\156', '\147', + '\040', '\061', '\012', '\156', '\142', '\132', '\040', '\141', + '\156', '\040', '\061', '\012', '\105', '\172', '\170', '\040', + '\163', '\172', '\040', '\061', '\012', '\167', '\116', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\130', '\162', + '\152', '\040', '\145', '\162', '\040', '\061', '\012', '\143', + '\170', '\130', '\040', '\143', '\150', '\040', '\061', '\012', + '\144', '\121', '\160', '\040', '\144', '\145', '\040', '\061', + '\012', '\131', '\160', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\160', '\116', '\160', '\040', '\160', '\162', + '\040', '\061', '\012', '\160', '\142', '\121', '\040', '\160', + '\162', '\040', '\061', '\012', '\147', '\115', '\166', '\040', + '\156', '\147', '\040', '\061', '\012', '\161', '\145', '\106', + '\040', '\161', '\165', '\040', '\061', '\012', '\165', '\126', + '\166', '\040', '\161', '\165', '\040', '\061', '\012', '\144', + '\126', '\153', '\040', '\144', '\145', '\040', '\061', '\012', + '\165', '\115', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\121', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\155', '\150', '\120', '\040', '\164', '\150', + '\040', '\061', '\012', '\151', '\124', '\142', '\040', '\151', + '\156', '\040', '\061', '\012', '\120', '\166', '\167', '\040', + '\166', '\141', '\040', '\061', '\012', '\172', '\103', '\167', + '\040', '\163', '\172', '\040', '\061', '\012', '\167', '\143', + '\122', '\040', '\143', '\150', '\040', '\061', '\012', '\163', + '\166', '\125', '\040', '\163', '\164', '\040', '\061', '\012', + '\156', '\115', '\172', '\040', '\141', '\156', '\040', '\061', + '\012', '\143', '\152', '\105', '\040', '\143', '\150', '\040', + '\061', '\012', '\152', '\155', '\110', '\040', '\151', '\152', + '\040', '\061', '\012', '\121', '\172', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\155', '\161', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\161', '\154', '\125', + '\040', '\161', '\165', '\040', '\061', '\012', '\132', '\166', + '\160', '\040', '\166', '\141', '\040', '\061', '\012', '\170', + '\110', '\154', '\040', '\154', '\145', '\040', '\061', '\012', + '\147', '\161', '\102', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\163', '\116', '\040', '\163', '\164', '\040', + '\061', '\012', '\153', '\103', '\152', '\040', '\151', '\152', + '\040', '\061', '\012', '\117', '\154', '\170', '\040', '\154', + '\145', '\040', '\061', '\012', '\107', '\170', '\167', '\040', + '\167', '\141', '\040', '\061', '\012', '\170', '\167', '\126', + '\040', '\167', '\141', '\040', '\061', '\012', '\146', '\120', + '\142', '\040', '\142', '\145', '\040', '\061', '\012', '\122', + '\150', '\166', '\040', '\164', '\150', '\040', '\061', '\012', + '\160', '\147', '\126', '\040', '\156', '\147', '\040', '\061', + '\012', '\121', '\144', '\160', '\040', '\144', '\145', '\040', + '\061', '\012', '\172', '\106', '\163', '\040', '\163', '\164', + '\040', '\061', '\012', '\153', '\154', '\121', '\040', '\154', + '\145', '\040', '\061', '\012', '\171', '\112', '\144', '\040', + '\144', '\145', '\040', '\061', '\012', '\162', '\170', '\105', + '\040', '\145', '\162', '\040', '\061', '\012', '\165', '\110', + '\166', '\040', '\161', '\165', '\040', '\061', '\012', '\167', + '\113', '\154', '\040', '\154', '\145', '\040', '\061', '\012', + '\167', '\160', '\112', '\040', '\160', '\162', '\040', '\061', + '\012', '\103', '\152', '\162', '\040', '\145', '\162', '\040', + '\061', '\012', '\164', '\131', '\147', '\040', '\164', '\150', + '\040', '\061', '\012', '\126', '\160', '\172', '\040', '\163', + '\172', '\040', '\061', '\012', '\132', '\170', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\160', '\121', '\154', + '\040', '\154', '\145', '\040', '\061', '\012', '\106', '\170', + '\145', '\040', '\145', '\162', '\040', '\061', '\012', '\121', + '\157', '\153', '\040', '\157', '\156', '\040', '\061', '\012', + '\160', '\154', '\113', '\040', '\154', '\145', '\040', '\061', + '\012', '\154', '\160', '\130', '\040', '\154', '\145', '\040', + '\061', '\012', '\152', '\144', '\120', '\040', '\144', '\145', + '\040', '\061', '\012', '\132', '\161', '\171', '\040', '\161', + '\165', '\040', '\061', '\012', '\171', '\122', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\156', '\104', '\147', + '\040', '\141', '\156', '\040', '\061', '\012', '\153', '\161', + '\114', '\040', '\161', '\165', '\040', '\061', '\012', '\165', + '\147', '\127', '\040', '\161', '\165', '\040', '\061', '\012', + '\115', '\142', '\146', '\040', '\142', '\145', '\040', '\061', + '\012', '\113', '\161', '\154', '\040', '\161', '\165', '\040', + '\061', '\012', '\116', '\161', '\167', '\040', '\161', '\165', + '\040', '\061', '\012', '\112', '\172', '\167', '\040', '\163', + '\172', '\040', '\061', '\012', '\163', '\107', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\167', '\104', '\166', + '\040', '\166', '\141', '\040', '\061', '\012', '\112', '\152', + '\153', '\040', '\151', '\152', '\040', '\061', '\012', '\172', + '\164', '\121', '\040', '\164', '\150', '\040', '\061', '\012', + '\150', '\167', '\120', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\104', '\160', '\040', '\160', '\162', '\040', + '\061', '\012', '\147', '\146', '\107', '\040', '\156', '\147', + '\040', '\061', '\012', '\161', '\150', '\114', '\040', '\164', + '\150', '\040', '\061', '\012', '\143', '\125', '\166', '\040', + '\143', '\150', '\040', '\061', '\012', '\127', '\142', '\153', + '\040', '\153', '\141', '\040', '\061', '\012', '\146', '\153', + '\106', '\040', '\153', '\157', '\040', '\061', '\012', '\120', + '\161', '\166', '\040', '\161', '\165', '\040', '\061', '\012', + '\156', '\142', '\113', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\123', '\172', '\040', '\161', '\165', '\040', + '\061', '\012', '\166', '\167', '\111', '\040', '\166', '\141', + '\040', '\061', '\012', '\143', '\106', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\161', '\146', '\107', '\040', + '\161', '\165', '\040', '\061', '\012', '\162', '\150', '\106', + '\040', '\164', '\150', '\040', '\061', '\012', '\170', '\172', + '\154', '\040', '\154', '\145', '\040', '\061', '\012', '\144', + '\116', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\172', '\167', '\122', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\172', '\113', '\040', '\163', '\172', '\040', + '\061', '\012', '\142', '\121', '\141', '\040', '\141', '\156', + '\040', '\061', '\012', '\150', '\114', '\161', '\040', '\164', + '\150', '\040', '\061', '\012', '\146', '\125', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\162', '\110', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\165', '\112', + '\152', '\040', '\161', '\165', '\040', '\061', '\012', '\106', + '\150', '\172', '\040', '\164', '\150', '\040', '\061', '\012', + '\116', '\172', '\155', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\122', '\172', '\040', '\156', '\147', '\040', + '\061', '\012', '\161', '\130', '\146', '\040', '\161', '\165', + '\040', '\061', '\012', '\124', '\172', '\155', '\040', '\163', + '\172', '\040', '\061', '\012', '\132', '\153', '\170', '\040', + '\153', '\141', '\040', '\061', '\012', '\150', '\114', '\170', + '\040', '\164', '\150', '\040', '\061', '\012', '\125', '\153', + '\144', '\040', '\144', '\145', '\040', '\061', '\012', '\146', + '\115', '\146', '\040', '\146', '\157', '\040', '\061', '\012', + '\166', '\107', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\152', '\164', '\111', '\040', '\164', '\150', '\040', + '\061', '\012', '\150', '\170', '\105', '\040', '\164', '\150', + '\040', '\061', '\012', '\152', '\162', '\110', '\040', '\145', + '\162', '\040', '\061', '\012', '\106', '\147', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\144', '\154', '\106', + '\040', '\154', '\145', '\040', '\061', '\012', '\152', '\143', + '\117', '\040', '\152', '\141', '\040', '\061', '\012', '\163', + '\103', '\167', '\040', '\163', '\164', '\040', '\061', '\012', + '\102', '\161', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\132', '\171', '\040', '\153', '\141', '\040', + '\061', '\012', '\146', '\117', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\162', '\112', '\142', '\040', '\145', + '\162', '\040', '\061', '\012', '\162', '\152', '\126', '\040', + '\145', '\162', '\040', '\061', '\012', '\113', '\167', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\110', '\143', + '\167', '\040', '\143', '\150', '\040', '\061', '\012', '\155', + '\103', '\167', '\040', '\155', '\141', '\040', '\061', '\012', + '\150', '\170', '\115', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\124', '\142', '\040', '\151', '\152', '\040', + '\061', '\012', '\155', '\155', '\121', '\040', '\155', '\145', + '\040', '\061', '\012', '\160', '\152', '\122', '\040', '\151', + '\152', '\040', '\061', '\012', '\143', '\144', '\120', '\040', + '\143', '\150', '\040', '\061', '\012', '\132', '\152', '\163', + '\040', '\163', '\164', '\040', '\061', '\012', '\152', '\161', + '\106', '\040', '\161', '\165', '\040', '\061', '\012', '\166', + '\115', '\156', '\040', '\141', '\156', '\040', '\061', '\012', + '\115', '\161', '\163', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\166', '\130', '\040', '\163', '\164', '\040', + '\061', '\012', '\151', '\130', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\156', '\167', '\122', '\040', '\141', + '\156', '\040', '\061', '\012', '\171', '\164', '\122', '\040', + '\164', '\150', '\040', '\061', '\012', '\126', '\152', '\142', + '\040', '\151', '\152', '\040', '\061', '\012', '\103', '\152', + '\154', '\040', '\154', '\145', '\040', '\061', '\012', '\160', + '\130', '\144', '\040', '\144', '\145', '\040', '\061', '\012', + '\107', '\167', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\111', '\152', '\040', '\161', '\165', '\040', + '\061', '\012', '\153', '\121', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\146', '\131', '\155', '\040', '\155', + '\145', '\040', '\061', '\012', '\166', '\164', '\132', '\040', + '\164', '\150', '\040', '\061', '\012', '\125', '\163', '\170', + '\040', '\163', '\164', '\040', '\061', '\012', '\156', '\146', + '\120', '\040', '\141', '\156', '\040', '\061', '\012', '\144', + '\121', '\170', '\040', '\144', '\145', '\040', '\061', '\012', + '\157', '\130', '\146', '\040', '\157', '\156', '\040', '\061', + '\012', '\146', '\105', '\167', '\040', '\167', '\141', '\040', + '\061', '\012', '\163', '\147', '\130', '\040', '\156', '\147', + '\040', '\061', '\012', '\143', '\120', '\160', '\040', '\143', + '\150', '\040', '\061', '\012', '\171', '\142', '\127', '\040', + '\142', '\145', '\040', '\061', '\012', '\153', '\143', '\127', + '\040', '\143', '\150', '\040', '\061', '\012', '\153', '\110', + '\146', '\040', '\153', '\141', '\040', '\061', '\012', '\166', + '\143', '\125', '\040', '\143', '\150', '\040', '\061', '\012', + '\164', '\130', '\157', '\040', '\164', '\150', '\040', '\061', + '\012', '\113', '\172', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\103', '\146', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\125', '\152', '\171', '\040', '\151', + '\152', '\040', '\061', '\012', '\106', '\170', '\141', '\040', + '\141', '\156', '\040', '\061', '\012', '\150', '\170', '\123', + '\040', '\164', '\150', '\040', '\061', '\012', '\164', '\127', + '\170', '\040', '\164', '\150', '\040', '\061', '\012', '\155', + '\154', '\113', '\040', '\154', '\145', '\040', '\061', '\012', + '\156', '\132', '\152', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\117', '\166', '\040', '\161', '\165', '\040', + '\061', '\012', '\130', '\153', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\106', '\172', '\146', '\040', '\163', + '\172', '\040', '\061', '\012', '\165', '\124', '\144', '\040', + '\161', '\165', '\040', '\061', '\012', '\161', '\162', '\123', + '\040', '\161', '\165', '\040', '\061', '\012', '\120', '\164', + '\167', '\040', '\164', '\150', '\040', '\061', '\012', '\144', + '\104', '\163', '\040', '\163', '\164', '\040', '\061', '\012', + '\162', '\116', '\155', '\040', '\145', '\162', '\040', '\061', + '\012', '\105', '\167', '\146', '\040', '\167', '\141', '\040', + '\061', '\012', '\150', '\112', '\153', '\040', '\164', '\150', + '\040', '\061', '\012', '\110', '\144', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\112', '\164', '\167', '\040', + '\164', '\150', '\040', '\061', '\012', '\153', '\161', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\156', '\110', + '\161', '\040', '\141', '\156', '\040', '\061', '\012', '\162', + '\150', '\110', '\040', '\164', '\150', '\040', '\061', '\012', + '\157', '\161', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\160', '\132', '\040', '\166', '\141', '\040', + '\061', '\012', '\104', '\147', '\144', '\040', '\156', '\147', + '\040', '\061', '\012', '\161', '\170', '\126', '\040', '\161', + '\165', '\040', '\061', '\012', '\103', '\170', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\160', '\154', '\126', + '\040', '\160', '\162', '\040', '\061', '\012', '\153', '\111', + '\151', '\040', '\151', '\156', '\040', '\061', '\012', '\113', + '\150', '\143', '\040', '\164', '\150', '\040', '\061', '\012', + '\152', '\163', '\131', '\040', '\163', '\164', '\040', '\061', + '\012', '\146', '\114', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\131', '\153', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\121', '\155', '\170', '\040', '\155', + '\145', '\040', '\061', '\012', '\172', '\166', '\111', '\040', + '\163', '\172', '\040', '\061', '\012', '\171', '\150', '\123', + '\040', '\164', '\150', '\040', '\061', '\012', '\161', '\146', + '\147', '\040', '\161', '\165', '\040', '\061', '\012', '\167', + '\170', '\132', '\040', '\167', '\141', '\040', '\061', '\012', + '\152', '\126', '\171', '\040', '\151', '\152', '\040', '\061', + '\012', '\153', '\121', '\167', '\040', '\153', '\141', '\040', + '\061', '\012', '\172', '\130', '\166', '\040', '\163', '\172', + '\040', '\061', '\012', '\114', '\150', '\163', '\040', '\164', + '\150', '\040', '\061', '\012', '\115', '\153', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\152', '\153', '\125', + '\040', '\151', '\152', '\040', '\061', '\012', '\131', '\150', + '\161', '\040', '\164', '\150', '\040', '\061', '\012', '\172', + '\162', '\110', '\040', '\145', '\162', '\040', '\061', '\012', + '\166', '\150', '\107', '\040', '\166', '\141', '\040', '\061', + '\012', '\144', '\162', '\104', '\040', '\145', '\162', '\040', + '\061', '\012', '\120', '\163', '\152', '\040', '\163', '\164', + '\040', '\061', '\012', '\147', '\104', '\146', '\040', '\156', + '\147', '\040', '\061', '\012', '\130', '\152', '\152', '\040', + '\151', '\152', '\040', '\061', '\012', '\160', '\114', '\155', + '\040', '\155', '\145', '\040', '\061', '\012', '\153', '\154', + '\103', '\040', '\154', '\145', '\040', '\061', '\012', '\150', + '\124', '\170', '\040', '\164', '\150', '\040', '\061', '\012', + '\172', '\162', '\112', '\040', '\145', '\162', '\040', '\061', + '\012', '\130', '\147', '\153', '\040', '\156', '\147', '\040', + '\061', '\012', '\127', '\170', '\146', '\040', '\146', '\157', + '\040', '\061', '\012', '\146', '\144', '\104', '\040', '\144', + '\145', '\040', '\061', '\012', '\152', '\110', '\160', '\040', + '\151', '\152', '\040', '\061', '\012', '\171', '\104', '\167', + '\040', '\167', '\141', '\040', '\061', '\012', '\153', '\120', + '\166', '\040', '\153', '\141', '\040', '\061', '\012', '\122', + '\153', '\155', '\040', '\153', '\141', '\040', '\061', '\012', + '\155', '\172', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\154', '\110', '\172', '\040', '\154', '\145', '\040', + '\061', '\012', '\166', '\160', '\122', '\040', '\166', '\141', + '\040', '\061', '\012', '\167', '\132', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\160', '\102', '\144', '\040', + '\144', '\145', '\040', '\061', '\012', '\161', '\120', '\146', + '\040', '\161', '\165', '\040', '\061', '\012', '\150', '\116', + '\167', '\040', '\164', '\150', '\040', '\061', '\012', '\116', + '\166', '\152', '\040', '\151', '\152', '\040', '\061', '\012', + '\160', '\171', '\125', '\040', '\160', '\162', '\040', '\061', + '\012', '\123', '\152', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\113', '\172', '\170', '\040', '\163', '\172', + '\040', '\061', '\012', '\157', '\121', '\160', '\040', '\157', + '\156', '\040', '\061', '\012', '\170', '\144', '\114', '\040', + '\144', '\145', '\040', '\061', '\012', '\144', '\156', '\132', + '\040', '\141', '\156', '\040', '\061', '\012', '\161', '\146', + '\102', '\040', '\161', '\165', '\040', '\061', '\012', '\153', + '\112', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\146', '\127', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\130', '\155', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\162', '\107', '\170', '\040', '\145', '\162', + '\040', '\061', '\012', '\163', '\106', '\146', '\040', '\163', + '\164', '\040', '\061', '\012', '\126', '\167', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\164', '\113', '\144', + '\040', '\164', '\150', '\040', '\061', '\012', '\163', '\121', + '\170', '\040', '\163', '\164', '\040', '\061', '\012', '\157', + '\116', '\155', '\040', '\157', '\156', '\040', '\061', '\012', + '\165', '\130', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\163', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\171', '\127', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\150', '\146', '\103', '\040', '\164', + '\150', '\040', '\061', '\012', '\111', '\152', '\144', '\040', + '\144', '\145', '\040', '\061', '\012', '\144', '\153', '\127', + '\040', '\144', '\145', '\040', '\061', '\012', '\116', '\170', + '\156', '\040', '\141', '\156', '\040', '\061', '\012', '\152', + '\165', '\103', '\040', '\161', '\165', '\040', '\061', '\012', + '\142', '\120', '\171', '\040', '\142', '\145', '\040', '\061', + '\012', '\154', '\113', '\163', '\040', '\154', '\145', '\040', + '\061', '\012', '\141', '\114', '\161', '\040', '\141', '\156', + '\040', '\061', '\012', '\152', '\120', '\160', '\040', '\151', + '\152', '\040', '\061', '\012', '\167', '\160', '\132', '\040', + '\160', '\162', '\040', '\061', '\012', '\146', '\152', '\105', + '\040', '\151', '\152', '\040', '\061', '\012', '\172', '\116', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\155', + '\150', '\116', '\040', '\164', '\150', '\040', '\061', '\012', + '\142', '\121', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\170', '\102', '\040', '\142', '\145', '\040', + '\061', '\012', '\146', '\144', '\130', '\040', '\144', '\145', + '\040', '\061', '\012', '\112', '\143', '\166', '\040', '\166', + '\141', '\040', '\061', '\012', '\106', '\144', '\160', '\040', + '\144', '\145', '\040', '\061', '\012', '\167', '\126', '\170', + '\040', '\167', '\141', '\040', '\061', '\012', '\164', '\155', + '\125', '\040', '\164', '\150', '\040', '\061', '\012', '\156', + '\152', '\112', '\040', '\141', '\156', '\040', '\061', '\012', + '\161', '\172', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\164', '\104', '\040', '\164', '\150', '\040', + '\061', '\012', '\142', '\143', '\130', '\040', '\143', '\150', + '\040', '\061', '\012', '\107', '\150', '\170', '\040', '\164', + '\150', '\040', '\061', '\012', '\170', '\132', '\152', '\040', + '\151', '\152', '\040', '\061', '\012', '\166', '\113', '\167', + '\040', '\166', '\141', '\040', '\061', '\012', '\160', '\166', + '\117', '\040', '\166', '\141', '\040', '\061', '\012', '\147', + '\130', '\163', '\040', '\156', '\147', '\040', '\061', '\012', + '\167', '\122', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\147', '\116', '\040', '\164', '\150', '\040', + '\061', '\012', '\147', '\160', '\117', '\040', '\156', '\147', + '\040', '\061', '\012', '\150', '\127', '\143', '\040', '\164', + '\150', '\040', '\061', '\012', '\125', '\160', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\166', '\167', '\104', + '\040', '\166', '\141', '\040', '\061', '\012', '\155', '\170', + '\105', '\040', '\155', '\145', '\040', '\061', '\012', '\132', + '\166', '\155', '\040', '\166', '\141', '\040', '\061', '\012', + '\157', '\172', '\115', '\040', '\157', '\156', '\040', '\061', + '\012', '\146', '\142', '\112', '\040', '\142', '\145', '\040', + '\061', '\012', '\164', '\160', '\121', '\040', '\164', '\150', + '\040', '\061', '\012', '\171', '\145', '\126', '\040', '\145', + '\162', '\040', '\061', '\012', '\132', '\156', '\142', '\040', + '\141', '\156', '\040', '\061', '\012', '\167', '\130', '\166', + '\040', '\166', '\141', '\040', '\061', '\012', '\142', '\143', + '\131', '\040', '\143', '\150', '\040', '\061', '\012', '\163', + '\147', '\132', '\040', '\156', '\147', '\040', '\061', '\012', + '\161', '\146', '\115', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\143', '\114', '\040', '\143', '\150', '\040', + '\061', '\012', '\155', '\130', '\154', '\040', '\154', '\145', + '\040', '\061', '\012', '\165', '\102', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\152', '\170', '\127', '\040', + '\151', '\152', '\040', '\061', '\012', '\155', '\164', '\125', + '\040', '\164', '\150', '\040', '\061', '\012', '\161', '\147', + '\112', '\040', '\161', '\165', '\040', '\061', '\012', '\144', + '\101', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\152', '\102', '\166', '\040', '\151', '\152', '\040', '\061', + '\012', '\107', '\164', '\171', '\040', '\164', '\150', '\040', + '\061', '\012', '\112', '\146', '\155', '\040', '\155', '\145', + '\040', '\061', '\012', '\170', '\161', '\121', '\040', '\161', + '\165', '\040', '\061', '\012', '\143', '\102', '\160', '\040', + '\143', '\150', '\040', '\061', '\012', '\130', '\161', '\144', + '\040', '\161', '\165', '\040', '\061', '\012', '\146', '\166', + '\115', '\040', '\166', '\141', '\040', '\061', '\012', '\165', + '\127', '\155', '\040', '\161', '\165', '\040', '\061', '\012', + '\162', '\123', '\142', '\040', '\145', '\162', '\040', '\061', + '\012', '\130', '\161', '\152', '\040', '\161', '\165', '\040', + '\061', '\012', '\161', '\124', '\144', '\040', '\161', '\165', + '\040', '\061', '\012', '\154', '\114', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\112', '\162', '\160', '\040', + '\145', '\162', '\040', '\061', '\012', '\157', '\112', '\142', + '\040', '\157', '\156', '\040', '\061', '\012', '\160', '\130', + '\171', '\040', '\160', '\162', '\040', '\061', '\012', '\172', + '\162', '\121', '\040', '\145', '\162', '\040', '\061', '\012', + '\143', '\156', '\124', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\163', '\105', '\040', '\161', '\165', '\040', + '\061', '\012', '\160', '\132', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\142', '\126', '\171', '\040', '\142', + '\145', '\040', '\061', '\012', '\161', '\111', '\172', '\040', + '\161', '\165', '\040', '\061', '\012', '\144', '\147', '\122', + '\040', '\156', '\147', '\040', '\061', '\012', '\155', '\114', + '\166', '\040', '\166', '\141', '\040', '\061', '\012', '\150', + '\126', '\154', '\040', '\164', '\150', '\040', '\061', '\012', + '\161', '\122', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\150', '\101', '\040', '\164', '\150', '\040', + '\061', '\012', '\172', '\114', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\123', '\147', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\160', '\114', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\124', '\170', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\171', '\160', + '\131', '\040', '\160', '\162', '\040', '\061', '\012', '\164', + '\130', '\172', '\040', '\164', '\150', '\040', '\061', '\012', + '\144', '\143', '\103', '\040', '\143', '\150', '\040', '\061', + '\012', '\151', '\131', '\146', '\040', '\151', '\156', '\040', + '\061', '\012', '\127', '\167', '\155', '\040', '\155', '\145', + '\040', '\061', '\012', '\153', '\132', '\153', '\040', '\153', + '\141', '\040', '\061', '\012', '\131', '\167', '\162', '\040', + '\145', '\162', '\040', '\061', '\012', '\147', '\106', '\166', + '\040', '\156', '\147', '\040', '\061', '\012', '\106', '\155', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\165', + '\121', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\170', '\167', '\122', '\040', '\167', '\141', '\040', '\061', + '\012', '\131', '\146', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\141', '\111', '\157', '\040', '\141', '\156', + '\040', '\061', '\012', '\163', '\102', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\107', '\172', '\142', '\040', + '\163', '\172', '\040', '\061', '\012', '\152', '\167', '\111', + '\040', '\151', '\152', '\040', '\061', '\012', '\143', '\106', + '\146', '\040', '\143', '\150', '\040', '\061', '\012', '\141', + '\127', '\166', '\040', '\141', '\156', '\040', '\061', '\012', + '\105', '\141', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\153', '\127', '\040', '\153', '\141', '\040', + '\061', '\012', '\116', '\146', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\146', '\154', '\116', '\040', '\154', + '\145', '\040', '\061', '\012', '\114', '\160', '\155', '\040', + '\155', '\145', '\040', '\061', '\012', '\171', '\154', '\113', + '\040', '\154', '\145', '\040', '\061', '\012', '\132', '\156', + '\162', '\040', '\141', '\156', '\040', '\061', '\012', '\155', + '\143', '\121', '\040', '\143', '\150', '\040', '\061', '\012', + '\153', '\146', '\105', '\040', '\153', '\141', '\040', '\061', + '\012', '\111', '\171', '\146', '\040', '\156', '\171', '\040', + '\061', '\012', '\161', '\162', '\126', '\040', '\161', '\165', + '\040', '\061', '\012', '\146', '\120', '\170', '\040', '\146', + '\157', '\040', '\061', '\012', '\146', '\147', '\112', '\040', + '\156', '\147', '\040', '\061', '\012', '\152', '\111', '\151', + '\040', '\151', '\156', '\040', '\061', '\012', '\142', '\120', + '\167', '\040', '\167', '\141', '\040', '\061', '\012', '\121', + '\171', '\170', '\040', '\156', '\171', '\040', '\061', '\012', + '\121', '\156', '\142', '\040', '\141', '\156', '\040', '\061', + '\012', '\127', '\144', '\155', '\040', '\144', '\145', '\040', + '\061', '\012', '\156', '\112', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\161', '\103', '\144', '\040', '\161', + '\165', '\040', '\061', '\012', '\147', '\132', '\154', '\040', + '\156', '\147', '\040', '\061', '\012', '\116', '\154', '\172', + '\040', '\154', '\145', '\040', '\061', '\012', '\132', '\167', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\151', + '\127', '\154', '\040', '\151', '\156', '\040', '\061', '\012', + '\142', '\125', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\142', '\112', '\040', '\154', '\145', '\040', + '\061', '\012', '\163', '\116', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\161', '\152', '\125', '\040', '\161', + '\165', '\040', '\061', '\012', '\167', '\142', '\124', '\040', + '\167', '\141', '\040', '\061', '\012', '\171', '\116', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\155', '\170', + '\115', '\040', '\155', '\145', '\040', '\061', '\012', '\160', + '\110', '\153', '\040', '\153', '\141', '\040', '\061', '\012', + '\122', '\144', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\153', '\105', '\040', '\156', '\147', '\040', + '\061', '\012', '\150', '\142', '\116', '\040', '\164', '\150', + '\040', '\061', '\012', '\124', '\147', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\147', '\152', '\126', '\040', + '\156', '\147', '\040', '\061', '\012', '\107', '\152', '\167', + '\040', '\151', '\152', '\040', '\061', '\012', '\147', '\161', + '\130', '\040', '\161', '\165', '\040', '\061', '\012', '\161', + '\130', '\170', '\040', '\161', '\165', '\040', '\061', '\012', + '\166', '\121', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\116', '\142', '\040', '\160', '\162', '\040', + '\061', '\012', '\146', '\112', '\171', '\040', '\156', '\171', + '\040', '\061', '\012', '\171', '\166', '\132', '\040', '\166', + '\141', '\040', '\061', '\012', '\172', '\116', '\154', '\040', + '\154', '\145', '\040', '\061', '\012', '\172', '\104', '\142', + '\040', '\163', '\172', '\040', '\061', '\012', '\154', '\125', + '\172', '\040', '\154', '\145', '\040', '\061', '\012', '\104', + '\170', '\171', '\040', '\156', '\171', '\040', '\061', '\012', + '\127', '\167', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\150', '\120', '\156', '\040', '\164', '\150', '\040', + '\061', '\012', '\153', '\116', '\142', '\040', '\153', '\157', + '\040', '\061', '\012', '\127', '\144', '\142', '\040', '\144', + '\145', '\040', '\061', '\012', '\172', '\130', '\164', '\040', + '\164', '\150', '\040', '\061', '\012', '\160', '\152', '\114', + '\040', '\151', '\152', '\040', '\061', '\012', '\164', '\112', + '\147', '\040', '\164', '\150', '\040', '\061', '\012', '\152', + '\155', '\115', '\040', '\151', '\152', '\040', '\061', '\012', + '\142', '\130', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\124', '\166', '\040', '\164', '\150', '\040', + '\061', '\012', '\131', '\163', '\146', '\040', '\163', '\164', + '\040', '\061', '\012', '\150', '\155', '\121', '\040', '\164', + '\150', '\040', '\061', '\012', '\126', '\171', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\106', '\160', '\144', + '\040', '\144', '\145', '\040', '\061', '\012', '\171', '\121', + '\167', '\040', '\167', '\141', '\040', '\061', '\012', '\120', + '\142', '\156', '\040', '\141', '\156', '\040', '\061', '\012', + '\170', '\126', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\167', '\150', '\120', '\040', '\164', '\150', '\040', + '\061', '\012', '\146', '\123', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\107', '\170', '\172', '\040', '\172', + '\145', '\040', '\061', '\012', '\104', '\146', '\167', '\040', + '\167', '\141', '\040', '\061', '\012', '\162', '\115', '\170', + '\040', '\145', '\162', '\040', '\061', '\012', '\172', '\115', + '\146', '\040', '\163', '\172', '\040', '\061', '\012', '\166', + '\112', '\167', '\040', '\166', '\141', '\040', '\061', '\012', + '\170', '\112', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\146', '\116', '\040', '\146', '\157', '\040', + '\061', '\012', '\144', '\121', '\167', '\040', '\144', '\145', + '\040', '\061', '\012', '\146', '\165', '\104', '\040', '\161', + '\165', '\040', '\061', '\012', '\170', '\152', '\102', '\040', + '\151', '\152', '\040', '\061', '\012', '\154', '\120', '\152', + '\040', '\154', '\145', '\040', '\061', '\012', '\155', '\161', + '\101', '\040', '\161', '\165', '\040', '\061', '\012', '\155', + '\146', '\115', '\040', '\155', '\145', '\040', '\061', '\012', + '\153', '\167', '\107', '\040', '\153', '\141', '\040', '\061', + '\012', '\145', '\141', '\131', '\040', '\141', '\156', '\040', + '\061', '\012', '\126', '\155', '\155', '\040', '\155', '\145', + '\040', '\061', '\012', '\172', '\146', '\123', '\040', '\163', + '\172', '\040', '\061', '\012', '\106', '\155', '\171', '\040', + '\155', '\145', '\040', '\061', '\012', '\163', '\161', '\120', + '\040', '\161', '\165', '\040', '\061', '\012', '\146', '\113', + '\153', '\040', '\153', '\141', '\040', '\061', '\012', '\121', + '\144', '\166', '\040', '\144', '\145', '\040', '\061', '\012', + '\144', '\152', '\132', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\162', '\122', '\040', '\161', '\165', '\040', + '\061', '\012', '\164', '\170', '\113', '\040', '\164', '\150', + '\040', '\061', '\012', '\142', '\170', '\110', '\040', '\142', + '\145', '\040', '\061', '\012', '\152', '\122', '\142', '\040', + '\151', '\152', '\040', '\061', '\012', '\143', '\152', '\104', + '\040', '\143', '\150', '\040', '\061', '\012', '\123', '\170', + '\167', '\040', '\167', '\141', '\040', '\061', '\012', '\123', + '\170', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\166', '\162', '\132', '\040', '\145', '\162', '\040', '\061', + '\012', '\170', '\155', '\110', '\040', '\155', '\145', '\040', + '\061', '\012', '\144', '\146', '\110', '\040', '\144', '\145', + '\040', '\061', '\012', '\146', '\112', '\167', '\040', '\167', + '\141', '\040', '\061', '\012', '\155', '\167', '\132', '\040', + '\155', '\145', '\040', '\061', '\012', '\166', '\122', '\155', + '\040', '\166', '\141', '\040', '\061', '\012', '\170', '\167', + '\152', '\040', '\151', '\152', '\040', '\061', '\012', '\130', + '\161', '\162', '\040', '\145', '\162', '\040', '\061', '\012', + '\107', '\166', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\150', '\172', '\106', '\040', '\164', '\150', '\040', + '\061', '\012', '\170', '\156', '\113', '\040', '\141', '\156', + '\040', '\061', '\012', '\170', '\150', '\125', '\040', '\164', + '\150', '\040', '\061', '\012', '\116', '\154', '\163', '\040', + '\154', '\145', '\040', '\061', '\012', '\172', '\142', '\126', + '\040', '\163', '\172', '\040', '\061', '\012', '\146', '\124', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\127', + '\170', '\166', '\040', '\166', '\141', '\040', '\061', '\012', + '\165', '\160', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\101', '\157', '\040', '\161', '\165', '\040', + '\061', '\012', '\153', '\113', '\170', '\040', '\153', '\141', + '\040', '\061', '\012', '\172', '\154', '\104', '\040', '\154', + '\145', '\040', '\061', '\012', '\150', '\124', '\154', '\040', + '\164', '\150', '\040', '\061', '\012', '\107', '\161', '\162', + '\040', '\161', '\165', '\040', '\061', '\012', '\107', '\170', + '\155', '\040', '\155', '\145', '\040', '\061', '\012', '\172', + '\120', '\152', '\040', '\163', '\172', '\040', '\061', '\012', + '\142', '\166', '\132', '\040', '\166', '\141', '\040', '\061', + '\012', '\152', '\110', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\151', '\130', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\113', '\147', '\172', '\040', '\156', + '\147', '\040', '\061', '\012', '\112', '\171', '\151', '\040', + '\151', '\156', '\040', '\061', '\012', '\166', '\106', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\171', '\164', + '\127', '\040', '\164', '\150', '\040', '\061', '\012', '\161', + '\102', '\144', '\040', '\161', '\165', '\040', '\061', '\012', + '\130', '\152', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\147', '\117', '\040', '\156', '\147', '\040', + '\061', '\012', '\155', '\152', '\116', '\040', '\151', '\152', + '\040', '\061', '\012', '\104', '\152', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\172', '\111', '\152', '\040', + '\163', '\172', '\040', '\061', '\012', '\165', '\104', '\170', + '\040', '\161', '\165', '\040', '\061', '\012', '\161', '\112', + '\146', '\040', '\161', '\165', '\040', '\061', '\012', '\146', + '\101', '\170', '\040', '\146', '\157', '\040', '\061', '\012', + '\106', '\163', '\152', '\040', '\163', '\164', '\040', '\061', + '\012', '\171', '\104', '\146', '\040', '\156', '\171', '\040', + '\061', '\012', '\170', '\152', '\126', '\040', '\151', '\152', + '\040', '\061', '\012', '\150', '\144', '\102', '\040', '\164', + '\150', '\040', '\061', '\012', '\144', '\167', '\107', '\040', + '\144', '\145', '\040', '\061', '\012', '\163', '\154', '\127', + '\040', '\154', '\145', '\040', '\061', '\012', '\172', '\131', + '\142', '\040', '\163', '\172', '\040', '\061', '\012', '\166', + '\172', '\117', '\040', '\163', '\172', '\040', '\061', '\012', + '\166', '\161', '\117', '\040', '\161', '\165', '\040', '\061', + '\012', '\112', '\172', '\166', '\040', '\163', '\172', '\040', + '\061', '\012', '\170', '\155', '\107', '\040', '\155', '\145', + '\040', '\061', '\012', '\113', '\144', '\167', '\040', '\144', + '\145', '\040', '\061', '\012', '\170', '\126', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\152', '\164', '\105', + '\040', '\164', '\150', '\040', '\061', '\012', '\153', '\112', + '\171', '\040', '\153', '\141', '\040', '\061', '\012', '\170', + '\152', '\127', '\040', '\151', '\152', '\040', '\061', '\012', + '\155', '\167', '\122', '\040', '\155', '\145', '\040', '\061', + '\012', '\172', '\126', '\170', '\040', '\163', '\172', '\040', + '\061', '\012', '\164', '\115', '\152', '\040', '\164', '\150', + '\040', '\061', '\012', '\161', '\161', '\142', '\040', '\161', + '\165', '\040', '\061', '\012', '\156', '\154', '\121', '\040', + '\154', '\145', '\040', '\061', '\012', '\142', '\170', '\121', + '\040', '\142', '\145', '\040', '\061', '\012', '\150', '\112', + '\166', '\040', '\164', '\150', '\040', '\061', '\012', '\152', + '\156', '\131', '\040', '\141', '\156', '\040', '\061', '\012', + '\171', '\146', '\123', '\040', '\156', '\171', '\040', '\061', + '\012', '\115', '\144', '\167', '\040', '\144', '\145', '\040', + '\061', '\012', '\172', '\132', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\171', '\163', '\112', '\040', '\163', + '\164', '\040', '\061', '\012', '\121', '\161', '\166', '\040', + '\161', '\165', '\040', '\061', '\012', '\172', '\170', '\154', + '\040', '\154', '\145', '\040', '\061', '\012', '\152', '\101', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\154', + '\112', '\167', '\040', '\154', '\145', '\040', '\061', '\012', + '\153', '\167', '\112', '\040', '\153', '\141', '\040', '\061', + '\012', '\163', '\170', '\103', '\040', '\163', '\164', '\040', + '\061', '\012', '\150', '\112', '\162', '\040', '\164', '\150', + '\040', '\061', '\012', '\170', '\107', '\160', '\040', '\160', + '\162', '\040', '\061', '\012', '\143', '\143', '\106', '\040', + '\143', '\150', '\040', '\061', '\012', '\166', '\107', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\161', '\123', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\146', + '\161', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\153', '\153', '\126', '\040', '\153', '\141', '\040', '\061', + '\012', '\147', '\126', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\127', '\161', '\147', '\040', '\161', '\165', + '\040', '\061', '\012', '\153', '\112', '\160', '\040', '\153', + '\141', '\040', '\061', '\012', '\127', '\154', '\162', '\040', + '\145', '\162', '\040', '\061', '\012', '\112', '\167', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\161', '\105', + '\141', '\040', '\141', '\156', '\040', '\061', '\012', '\153', + '\162', '\114', '\040', '\145', '\162', '\040', '\061', '\012', + '\164', '\161', '\105', '\040', '\164', '\150', '\040', '\061', + '\012', '\145', '\112', '\172', '\040', '\145', '\162', '\040', + '\061', '\012', '\127', '\150', '\170', '\040', '\164', '\150', + '\040', '\061', '\012', '\166', '\127', '\167', '\040', '\166', + '\141', '\040', '\061', '\012', '\121', '\172', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\160', '\143', '\106', + '\040', '\143', '\150', '\040', '\061', '\012', '\126', '\155', + '\170', '\040', '\155', '\145', '\040', '\061', '\012', '\144', + '\166', '\103', '\040', '\144', '\145', '\040', '\061', '\012', + '\161', '\152', '\132', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\153', '\106', '\040', '\153', '\141', '\040', + '\061', '\012', '\143', '\166', '\117', '\040', '\143', '\150', + '\040', '\061', '\012', '\121', '\171', '\166', '\040', '\166', + '\141', '\040', '\061', '\012', '\150', '\116', '\163', '\040', + '\164', '\150', '\040', '\061', '\012', '\163', '\156', '\112', + '\040', '\141', '\156', '\040', '\061', '\012', '\171', '\152', + '\125', '\040', '\151', '\152', '\040', '\061', '\012', '\131', + '\146', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\170', '\114', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\162', '\126', '\172', '\040', '\145', '\162', '\040', + '\061', '\012', '\147', '\117', '\167', '\040', '\156', '\147', + '\040', '\061', '\012', '\146', '\170', '\114', '\040', '\146', + '\157', '\040', '\061', '\012', '\163', '\156', '\127', '\040', + '\141', '\156', '\040', '\061', '\012', '\171', '\127', '\153', + '\040', '\153', '\141', '\040', '\061', '\012', '\167', '\147', + '\113', '\040', '\156', '\147', '\040', '\061', '\012', '\141', + '\124', '\146', '\040', '\141', '\156', '\040', '\061', '\012', + '\145', '\126', '\146', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\132', '\160', '\040', '\166', '\141', '\040', + '\061', '\012', '\165', '\126', '\160', '\040', '\161', '\165', + '\040', '\061', '\012', '\126', '\152', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\172', '\167', '\124', '\040', + '\163', '\172', '\040', '\061', '\012', '\167', '\123', '\156', + '\040', '\141', '\156', '\040', '\061', '\012', '\156', '\116', + '\160', '\040', '\141', '\156', '\040', '\061', '\012', '\147', + '\146', '\106', '\040', '\156', '\147', '\040', '\061', '\012', + '\150', '\143', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\124', '\146', '\040', '\156', '\147', '\040', + '\061', '\012', '\161', '\141', '\112', '\040', '\141', '\156', + '\040', '\061', '\012', '\153', '\172', '\131', '\040', '\163', + '\172', '\040', '\061', '\012', '\154', '\152', '\130', '\040', + '\154', '\145', '\040', '\061', '\012', '\167', '\115', '\155', + '\040', '\155', '\145', '\040', '\061', '\012', '\142', '\164', + '\102', '\040', '\163', '\164', '\040', '\061', '\012', '\172', + '\146', '\105', '\040', '\163', '\172', '\040', '\061', '\012', + '\142', '\170', '\117', '\040', '\142', '\145', '\040', '\061', + '\012', '\167', '\120', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\146', '\147', '\113', '\040', '\156', '\147', + '\040', '\061', '\012', '\146', '\172', '\127', '\040', '\163', + '\172', '\040', '\061', '\012', '\144', '\143', '\130', '\040', + '\143', '\150', '\040', '\061', '\012', '\161', '\161', '\122', + '\040', '\161', '\165', '\040', '\061', '\012', '\153', '\152', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\166', + '\115', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\147', '\132', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\164', '\167', '\040', '\164', '\150', '\040', + '\061', '\012', '\166', '\153', '\131', '\040', '\153', '\141', + '\040', '\061', '\012', '\154', '\103', '\142', '\040', '\154', + '\145', '\040', '\061', '\012', '\144', '\160', '\117', '\040', + '\144', '\145', '\040', '\061', '\012', '\155', '\130', '\155', + '\040', '\155', '\145', '\040', '\061', '\012', '\166', '\127', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\146', + '\117', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\126', '\147', '\171', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\153', '\104', '\040', '\144', '\145', '\040', + '\061', '\012', '\146', '\121', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\166', '\111', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\154', '\132', '\162', '\040', + '\145', '\162', '\040', '\061', '\012', '\172', '\113', '\156', + '\040', '\141', '\156', '\040', '\061', '\012', '\126', '\160', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\104', + '\155', '\167', '\040', '\155', '\145', '\040', '\061', '\012', + '\116', '\167', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\153', '\131', '\154', '\040', '\154', '\145', '\040', + '\061', '\012', '\152', '\160', '\112', '\040', '\151', '\152', + '\040', '\061', '\012', '\161', '\130', '\151', '\040', '\161', + '\165', '\040', '\061', '\012', '\102', '\156', '\152', '\040', + '\141', '\156', '\040', '\061', '\012', '\170', '\146', '\113', + '\040', '\146', '\157', '\040', '\061', '\012', '\146', '\103', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\166', + '\120', '\144', '\040', '\144', '\145', '\040', '\061', '\012', + '\121', '\156', '\160', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\160', '\127', '\040', '\160', '\162', '\040', + '\061', '\012', '\165', '\167', '\112', '\040', '\161', '\165', + '\040', '\061', '\012', '\120', '\166', '\142', '\040', '\166', + '\141', '\040', '\061', '\012', '\143', '\156', '\103', '\040', + '\143', '\150', '\040', '\061', '\012', '\150', '\166', '\101', + '\040', '\164', '\150', '\040', '\061', '\012', '\150', '\107', + '\172', '\040', '\164', '\150', '\040', '\061', '\012', '\156', + '\132', '\170', '\040', '\141', '\156', '\040', '\061', '\012', + '\153', '\142', '\123', '\040', '\153', '\141', '\040', '\061', + '\012', '\123', '\167', '\170', '\040', '\167', '\141', '\040', + '\061', '\012', '\150', '\166', '\120', '\040', '\164', '\150', + '\040', '\061', '\012', '\153', '\161', '\107', '\040', '\161', + '\165', '\040', '\061', '\012', '\142', '\114', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\161', '\152', '\120', + '\040', '\161', '\165', '\040', '\061', '\012', '\163', '\125', + '\157', '\040', '\157', '\156', '\040', '\061', '\012', '\154', + '\104', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\132', '\154', '\160', '\040', '\154', '\145', '\040', '\061', + '\012', '\144', '\167', '\121', '\040', '\144', '\145', '\040', + '\061', '\012', '\144', '\154', '\116', '\040', '\154', '\145', + '\040', '\061', '\012', '\146', '\124', '\154', '\040', '\154', + '\145', '\040', '\061', '\012', '\116', '\160', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\142', '\115', '\156', + '\040', '\141', '\156', '\040', '\061', '\012', '\144', '\116', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\145', + '\146', '\126', '\040', '\145', '\162', '\040', '\061', '\012', + '\141', '\103', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\141', '\127', '\146', '\040', '\141', '\156', '\040', + '\061', '\012', '\114', '\161', '\157', '\040', '\161', '\165', + '\040', '\061', '\012', '\146', '\172', '\124', '\040', '\163', + '\172', '\040', '\061', '\012', '\112', '\152', '\162', '\040', + '\145', '\162', '\040', '\061', '\012', '\172', '\166', '\113', + '\040', '\163', '\172', '\040', '\061', '\012', '\156', '\167', + '\124', '\040', '\141', '\156', '\040', '\061', '\012', '\146', + '\130', '\162', '\040', '\145', '\162', '\040', '\061', '\012', + '\143', '\107', '\155', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\166', '\123', '\040', '\154', '\145', '\040', + '\061', '\012', '\161', '\104', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\161', '\122', '\155', '\040', '\161', + '\165', '\040', '\061', '\012', '\166', '\131', '\164', '\040', + '\164', '\150', '\040', '\061', '\012', '\151', '\121', '\166', + '\040', '\151', '\156', '\040', '\061', '\012', '\146', '\153', + '\110', '\040', '\153', '\141', '\040', '\061', '\012', '\146', + '\143', '\117', '\040', '\143', '\150', '\040', '\061', '\012', + '\162', '\116', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\155', '\123', '\040', '\161', '\165', '\040', + '\061', '\012', '\153', '\172', '\122', '\040', '\163', '\172', + '\040', '\061', '\012', '\104', '\146', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\161', '\125', '\163', '\040', + '\161', '\165', '\040', '\061', '\012', '\170', '\161', '\120', + '\040', '\161', '\165', '\040', '\061', '\012', '\163', '\130', + '\153', '\040', '\163', '\164', '\040', '\061', '\012', '\130', + '\171', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\160', '\127', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\142', '\114', '\040', '\151', '\152', '\040', + '\061', '\012', '\152', '\131', '\144', '\040', '\151', '\152', + '\040', '\061', '\012', '\153', '\161', '\126', '\040', '\161', + '\165', '\040', '\061', '\012', '\106', '\161', '\155', '\040', + '\161', '\165', '\040', '\061', '\012', '\170', '\157', '\130', + '\040', '\157', '\156', '\040', '\061', '\012', '\172', '\165', + '\130', '\040', '\161', '\165', '\040', '\061', '\012', '\170', + '\125', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\143', '\147', '\103', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\102', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\147', '\121', '\160', '\040', '\156', '\147', + '\040', '\061', '\012', '\152', '\156', '\105', '\040', '\141', + '\156', '\040', '\061', '\012', '\171', '\132', '\163', '\040', + '\163', '\164', '\040', '\061', '\012', '\146', '\153', '\104', + '\040', '\153', '\141', '\040', '\061', '\012', '\163', '\126', + '\153', '\040', '\163', '\164', '\040', '\061', '\012', '\161', + '\171', '\130', '\040', '\161', '\165', '\040', '\061', '\012', + '\143', '\102', '\146', '\040', '\143', '\150', '\040', '\061', + '\012', '\103', '\152', '\171', '\040', '\151', '\152', '\040', + '\061', '\012', '\144', '\120', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\167', '\104', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\144', '\170', '\102', '\040', + '\144', '\145', '\040', '\061', '\012', '\104', '\153', '\155', + '\040', '\153', '\141', '\040', '\061', '\012', '\153', '\120', + '\160', '\040', '\153', '\141', '\040', '\061', '\012', '\150', + '\127', '\172', '\040', '\164', '\150', '\040', '\061', '\012', + '\102', '\152', '\166', '\040', '\151', '\152', '\040', '\061', + '\012', '\111', '\172', '\146', '\040', '\163', '\172', '\040', + '\061', '\012', '\110', '\156', '\153', '\040', '\141', '\156', + '\040', '\061', '\012', '\162', '\121', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\112', '\167', '\165', '\040', + '\161', '\165', '\040', '\061', '\012', '\146', '\142', '\120', + '\040', '\142', '\145', '\040', '\061', '\012', '\146', '\162', + '\121', '\040', '\145', '\162', '\040', '\061', '\012', '\101', + '\157', '\166', '\040', '\157', '\156', '\040', '\061', '\012', + '\171', '\161', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\146', '\131', '\040', '\151', '\152', '\040', + '\061', '\012', '\170', '\163', '\110', '\040', '\163', '\164', + '\040', '\061', '\012', '\172', '\170', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\112', '\142', '\152', '\040', + '\151', '\152', '\040', '\061', '\012', '\115', '\152', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\147', '\122', + '\160', '\040', '\156', '\147', '\040', '\061', '\012', '\107', + '\166', '\167', '\040', '\166', '\141', '\040', '\061', '\012', + '\155', '\172', '\106', '\040', '\163', '\172', '\040', '\061', + '\012', '\157', '\161', '\106', '\040', '\161', '\165', '\040', + '\061', '\012', '\145', '\152', '\125', '\040', '\145', '\162', + '\040', '\061', '\012', '\170', '\155', '\121', '\040', '\155', + '\145', '\040', '\061', '\012', '\150', '\117', '\161', '\040', + '\164', '\150', '\040', '\061', '\012', '\160', '\167', '\130', + '\040', '\160', '\162', '\040', '\061', '\012', '\172', '\147', + '\113', '\040', '\156', '\147', '\040', '\061', '\012', '\167', + '\114', '\153', '\040', '\153', '\141', '\040', '\061', '\012', + '\146', '\161', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\120', '\155', '\040', '\144', '\145', '\040', + '\061', '\012', '\164', '\103', '\147', '\040', '\164', '\150', + '\040', '\061', '\012', '\161', '\162', '\106', '\040', '\161', + '\165', '\040', '\061', '\012', '\160', '\127', '\154', '\040', + '\154', '\145', '\040', '\061', '\012', '\162', '\104', '\146', + '\040', '\145', '\162', '\040', '\061', '\012', '\131', '\156', + '\167', '\040', '\141', '\156', '\040', '\061', '\012', '\152', + '\156', '\121', '\040', '\141', '\156', '\040', '\061', '\012', + '\164', '\106', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\160', '\125', '\040', '\145', '\162', '\040', + '\061', '\012', '\160', '\120', '\152', '\040', '\151', '\152', + '\040', '\061', '\012', '\171', '\152', '\115', '\040', '\151', + '\152', '\040', '\061', '\012', '\152', '\155', '\131', '\040', + '\151', '\152', '\040', '\061', '\012', '\103', '\160', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\165', '\104', + '\156', '\040', '\141', '\156', '\040', '\061', '\012', '\165', + '\161', '\131', '\040', '\161', '\165', '\040', '\061', '\012', + '\120', '\152', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\106', '\166', '\040', '\161', '\165', '\040', + '\061', '\012', '\113', '\164', '\146', '\040', '\164', '\150', + '\040', '\061', '\012', '\112', '\143', '\152', '\040', '\143', + '\150', '\040', '\061', '\012', '\153', '\160', '\117', '\040', + '\160', '\162', '\040', '\061', '\012', '\160', '\147', '\132', + '\040', '\156', '\147', '\040', '\061', '\012', '\153', '\146', + '\117', '\040', '\153', '\141', '\040', '\061', '\012', '\164', + '\132', '\166', '\040', '\164', '\150', '\040', '\061', '\012', + '\152', '\110', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\122', '\161', '\040', '\143', '\150', '\040', + '\061', '\012', '\172', '\104', '\155', '\040', '\163', '\172', + '\040', '\061', '\012', '\154', '\120', '\155', '\040', '\154', + '\145', '\040', '\061', '\012', '\163', '\166', '\120', '\040', + '\163', '\164', '\040', '\061', '\012', '\161', '\153', '\170', + '\040', '\161', '\165', '\040', '\061', '\012', '\142', '\116', + '\160', '\040', '\160', '\162', '\040', '\061', '\012', '\113', + '\152', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\166', '\161', '\123', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\121', '\160', '\040', '\160', '\162', '\040', + '\061', '\012', '\164', '\170', '\122', '\040', '\164', '\150', + '\040', '\061', '\012', '\110', '\160', '\146', '\040', '\160', + '\162', '\040', '\061', '\012', '\151', '\121', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\166', '\166', '\120', + '\040', '\166', '\141', '\040', '\061', '\012', '\151', '\107', + '\146', '\040', '\151', '\156', '\040', '\061', '\012', '\164', + '\152', '\111', '\040', '\164', '\150', '\040', '\061', '\012', + '\160', '\127', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\121', '\161', '\147', '\040', '\161', '\165', '\040', + '\061', '\012', '\161', '\151', '\106', '\040', '\164', '\151', + '\040', '\061', '\012', '\132', '\172', '\162', '\040', '\145', + '\162', '\040', '\061', '\012', '\141', '\131', '\146', '\040', + '\141', '\156', '\040', '\061', '\012', '\172', '\152', '\101', + '\040', '\163', '\172', '\040', '\061', '\012', '\153', '\167', + '\122', '\040', '\153', '\141', '\040', '\061', '\012', '\147', + '\153', '\115', '\040', '\156', '\147', '\040', '\061', '\012', + '\103', '\152', '\146', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\147', '\115', '\040', '\156', '\147', '\040', + '\061', '\012', '\122', '\170', '\153', '\040', '\153', '\141', + '\040', '\061', '\012', '\142', '\103', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\131', '\160', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\167', '\171', '\105', + '\040', '\167', '\141', '\040', '\061', '\012', '\151', '\171', + '\102', '\040', '\151', '\156', '\040', '\061', '\012', '\150', + '\121', '\160', '\040', '\164', '\150', '\040', '\061', '\012', + '\151', '\160', '\121', '\040', '\151', '\156', '\040', '\061', + '\012', '\125', '\143', '\152', '\040', '\143', '\150', '\040', + '\061', '\012', '\161', '\153', '\127', '\040', '\161', '\165', + '\040', '\061', '\012', '\153', '\162', '\113', '\040', '\145', + '\162', '\040', '\061', '\012', '\110', '\160', '\160', '\040', + '\160', '\162', '\040', '\061', '\012', '\170', '\156', '\116', + '\040', '\141', '\156', '\040', '\061', '\012', '\152', '\167', + '\102', '\040', '\151', '\152', '\040', '\061', '\012', '\132', + '\144', '\155', '\040', '\144', '\145', '\040', '\061', '\012', + '\155', '\131', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\164', '\121', '\170', '\040', '\164', '\150', '\040', + '\061', '\012', '\161', '\167', '\123', '\040', '\161', '\165', + '\040', '\061', '\012', '\110', '\170', '\157', '\040', '\157', + '\156', '\040', '\061', '\012', '\161', '\104', '\170', '\040', + '\161', '\165', '\040', '\061', '\012', '\143', '\130', '\144', + '\040', '\143', '\150', '\040', '\061', '\012', '\147', '\144', + '\117', '\040', '\156', '\147', '\040', '\061', '\012', '\141', + '\105', '\157', '\040', '\141', '\156', '\040', '\061', '\012', + '\124', '\167', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\141', '\166', '\121', '\040', '\141', '\156', '\040', + '\061', '\012', '\154', '\150', '\132', '\040', '\164', '\150', + '\040', '\061', '\012', '\154', '\172', '\126', '\040', '\154', + '\145', '\040', '\061', '\012', '\142', '\110', '\146', '\040', + '\142', '\145', '\040', '\061', '\012', '\142', '\112', '\156', + '\040', '\141', '\156', '\040', '\061', '\012', '\125', '\161', + '\172', '\040', '\161', '\165', '\040', '\061', '\012', '\165', + '\106', '\171', '\040', '\161', '\165', '\040', '\061', '\012', + '\152', '\116', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\102', '\160', '\040', '\160', '\162', '\040', + '\061', '\012', '\144', '\122', '\142', '\040', '\144', '\145', + '\040', '\061', '\012', '\156', '\154', '\124', '\040', '\141', + '\156', '\040', '\061', '\012', '\167', '\162', '\117', '\040', + '\145', '\162', '\040', '\061', '\012', '\154', '\172', '\127', + '\040', '\154', '\145', '\040', '\061', '\012', '\146', '\131', + '\146', '\040', '\146', '\157', '\040', '\061', '\012', '\155', + '\122', '\167', '\040', '\155', '\145', '\040', '\061', '\012', + '\162', '\130', '\171', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\171', '\122', '\040', '\161', '\165', '\040', + '\061', '\012', '\146', '\107', '\166', '\040', '\166', '\141', + '\040', '\061', '\012', '\125', '\167', '\153', '\040', '\153', + '\141', '\040', '\061', '\012', '\153', '\130', '\155', '\040', + '\153', '\141', '\040', '\061', '\012', '\150', '\112', '\171', + '\040', '\164', '\150', '\040', '\061', '\012', '\130', '\147', + '\166', '\040', '\156', '\147', '\040', '\061', '\012', '\170', + '\131', '\166', '\040', '\166', '\141', '\040', '\061', '\012', + '\171', '\131', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\170', '\172', '\103', '\040', '\163', '\172', '\040', + '\061', '\012', '\147', '\152', '\102', '\040', '\156', '\147', + '\040', '\061', '\012', '\152', '\172', '\111', '\040', '\163', + '\172', '\040', '\061', '\012', '\172', '\162', '\117', '\040', + '\145', '\162', '\040', '\061', '\012', '\164', '\161', '\106', + '\040', '\164', '\150', '\040', '\061', '\012', '\166', '\167', + '\115', '\040', '\166', '\141', '\040', '\061', '\012', '\172', + '\103', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\154', '\152', '\114', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\156', '\132', '\040', '\141', '\156', '\040', + '\061', '\012', '\145', '\104', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\121', '\166', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\160', '\146', '\114', '\040', + '\160', '\162', '\040', '\061', '\012', '\151', '\122', '\142', + '\040', '\151', '\156', '\040', '\061', '\012', '\147', '\144', + '\122', '\040', '\156', '\147', '\040', '\061', '\012', '\161', + '\101', '\166', '\040', '\161', '\165', '\040', '\061', '\012', + '\166', '\156', '\114', '\040', '\141', '\156', '\040', '\061', + '\012', '\155', '\153', '\124', '\040', '\153', '\141', '\040', + '\061', '\012', '\160', '\126', '\153', '\040', '\153', '\141', + '\040', '\061', '\012', '\170', '\113', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\152', '\116', '\153', '\040', + '\151', '\152', '\040', '\061', '\012', '\152', '\114', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\143', '\116', + '\160', '\040', '\143', '\150', '\040', '\061', '\012', '\164', + '\155', '\120', '\040', '\164', '\150', '\040', '\061', '\012', + '\166', '\126', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\146', '\120', '\040', '\161', '\165', '\040', + '\061', '\012', '\125', '\161', '\157', '\040', '\161', '\165', + '\040', '\061', '\012', '\104', '\156', '\160', '\040', '\141', + '\156', '\040', '\061', '\012', '\171', '\107', '\142', '\040', + '\142', '\145', '\040', '\061', '\012', '\163', '\110', '\144', + '\040', '\163', '\164', '\040', '\061', '\012', '\160', '\167', + '\106', '\040', '\160', '\162', '\040', '\061', '\012', '\146', + '\120', '\171', '\040', '\156', '\171', '\040', '\061', '\012', + '\104', '\162', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\112', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\163', '\121', '\160', '\040', '\163', '\164', + '\040', '\061', '\012', '\111', '\167', '\163', '\040', '\163', + '\164', '\040', '\061', '\012', '\165', '\103', '\167', '\040', + '\161', '\165', '\040', '\061', '\012', '\114', '\167', '\152', + '\040', '\151', '\152', '\040', '\061', '\012', '\162', '\106', + '\167', '\040', '\145', '\162', '\040', '\061', '\012', '\163', + '\112', '\160', '\040', '\163', '\164', '\040', '\061', '\012', + '\170', '\151', '\111', '\040', '\151', '\156', '\040', '\061', + '\012', '\122', '\161', '\166', '\040', '\161', '\165', '\040', + '\061', '\012', '\142', '\153', '\121', '\040', '\153', '\141', + '\040', '\061', '\012', '\161', '\116', '\160', '\040', '\161', + '\165', '\040', '\061', '\012', '\144', '\131', '\154', '\040', + '\154', '\145', '\040', '\061', '\012', '\126', '\155', '\146', + '\040', '\155', '\145', '\040', '\061', '\012', '\154', '\131', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\157', + '\120', '\167', '\040', '\157', '\156', '\040', '\061', '\012', + '\153', '\152', '\117', '\040', '\151', '\152', '\040', '\061', + '\012', '\155', '\113', '\142', '\040', '\155', '\145', '\040', + '\061', '\012', '\146', '\104', '\146', '\040', '\146', '\157', + '\040', '\061', '\012', '\146', '\106', '\142', '\040', '\142', + '\145', '\040', '\061', '\012', '\126', '\150', '\166', '\040', + '\164', '\150', '\040', '\061', '\012', '\110', '\152', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\161', '\146', + '\113', '\040', '\161', '\165', '\040', '\061', '\012', '\113', + '\152', '\160', '\040', '\151', '\152', '\040', '\061', '\012', + '\166', '\124', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\102', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\110', '\164', '\144', '\040', '\164', '\150', + '\040', '\061', '\012', '\160', '\116', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\142', '\121', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\141', '\123', '\170', + '\040', '\141', '\156', '\040', '\061', '\012', '\152', '\167', + '\170', '\040', '\151', '\152', '\040', '\061', '\012', '\125', + '\171', '\170', '\040', '\156', '\171', '\040', '\061', '\012', + '\167', '\126', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\111', '\157', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\116', '\150', '\155', '\040', '\164', '\150', + '\040', '\061', '\012', '\110', '\161', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\162', '\125', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\142', '\102', '\170', + '\040', '\142', '\145', '\040', '\061', '\012', '\107', '\161', + '\142', '\040', '\161', '\165', '\040', '\061', '\012', '\103', + '\143', '\167', '\040', '\143', '\150', '\040', '\061', '\012', + '\150', '\132', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\142', '\154', '\040', '\154', '\145', '\040', + '\061', '\012', '\170', '\106', '\166', '\040', '\166', '\141', + '\040', '\061', '\012', '\163', '\132', '\166', '\040', '\163', + '\164', '\040', '\061', '\012', '\161', '\172', '\131', '\040', + '\161', '\165', '\040', '\061', '\012', '\160', '\104', '\142', + '\040', '\160', '\162', '\040', '\061', '\012', '\143', '\146', + '\122', '\040', '\143', '\150', '\040', '\061', '\012', '\162', + '\161', '\153', '\040', '\161', '\165', '\040', '\061', '\012', + '\146', '\172', '\120', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\161', '\117', '\040', '\164', '\150', '\040', + '\061', '\012', '\160', '\172', '\110', '\040', '\163', '\172', + '\040', '\061', '\012', '\161', '\123', '\152', '\040', '\161', + '\165', '\040', '\061', '\012', '\160', '\170', '\112', '\040', + '\160', '\162', '\040', '\061', '\012', '\170', '\142', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\163', '\130', + '\146', '\040', '\163', '\164', '\040', '\061', '\012', '\171', + '\142', '\124', '\040', '\142', '\145', '\040', '\061', '\012', + '\163', '\110', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\124', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\120', '\147', '\146', '\040', '\156', '\147', + '\040', '\061', '\012', '\150', '\113', '\167', '\040', '\164', + '\150', '\040', '\061', '\012', '\152', '\120', '\152', '\040', + '\151', '\152', '\040', '\061', '\012', '\167', '\124', '\170', + '\040', '\167', '\141', '\040', '\061', '\012', '\152', '\123', + '\152', '\040', '\151', '\152', '\040', '\061', '\012', '\106', + '\147', '\172', '\040', '\156', '\147', '\040', '\061', '\012', + '\142', '\113', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\145', '\125', '\152', '\040', '\145', '\162', '\040', + '\061', '\012', '\143', '\104', '\146', '\040', '\143', '\150', + '\040', '\061', '\012', '\170', '\106', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\143', '\156', '\127', '\040', + '\141', '\156', '\040', '\061', '\012', '\164', '\125', '\171', + '\040', '\164', '\150', '\040', '\061', '\012', '\112', '\147', + '\170', '\040', '\156', '\147', '\040', '\061', '\012', '\171', + '\165', '\106', '\040', '\161', '\165', '\040', '\061', '\012', + '\166', '\171', '\121', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\103', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\152', '\122', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\143', '\130', '\170', '\040', '\143', + '\150', '\040', '\061', '\012', '\153', '\107', '\153', '\040', + '\153', '\141', '\040', '\061', '\012', '\130', '\156', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\161', '\120', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\154', + '\146', '\132', '\040', '\154', '\145', '\040', '\061', '\012', + '\161', '\126', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\167', '\163', '\040', '\163', '\164', '\040', + '\061', '\012', '\104', '\172', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\170', '\146', '\107', '\040', '\146', + '\157', '\040', '\061', '\012', '\146', '\130', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\152', '\147', '\126', + '\040', '\156', '\147', '\040', '\061', '\012', '\166', '\112', + '\152', '\040', '\151', '\152', '\040', '\061', '\012', '\142', + '\130', '\152', '\040', '\151', '\152', '\040', '\061', '\012', + '\143', '\147', '\107', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\165', '\127', '\040', '\161', '\165', '\040', + '\061', '\012', '\164', '\170', '\107', '\040', '\164', '\150', + '\040', '\061', '\012', '\132', '\170', '\172', '\040', '\163', + '\172', '\040', '\061', '\012', '\146', '\116', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\157', '\102', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\127', '\147', + '\166', '\040', '\156', '\147', '\040', '\061', '\012', '\110', + '\167', '\172', '\040', '\163', '\172', '\040', '\061', '\012', + '\157', '\141', '\127', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\122', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\165', '\130', '\172', '\040', '\161', '\165', + '\040', '\061', '\012', '\146', '\172', '\121', '\040', '\163', + '\172', '\040', '\061', '\012', '\142', '\143', '\102', '\040', + '\143', '\150', '\040', '\061', '\012', '\102', '\156', '\167', + '\040', '\141', '\156', '\040', '\061', '\012', '\147', '\166', + '\102', '\040', '\156', '\147', '\040', '\061', '\012', '\162', + '\121', '\155', '\040', '\145', '\162', '\040', '\061', '\012', + '\143', '\166', '\125', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\150', '\122', '\040', '\164', '\150', '\040', + '\061', '\012', '\172', '\170', '\122', '\040', '\163', '\172', + '\040', '\061', '\012', '\142', '\164', '\132', '\040', '\164', + '\150', '\040', '\061', '\012', '\113', '\153', '\146', '\040', + '\153', '\141', '\040', '\061', '\012', '\172', '\112', '\167', + '\040', '\163', '\172', '\040', '\061', '\012', '\165', '\167', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\160', + '\123', '\170', '\040', '\160', '\162', '\040', '\061', '\012', + '\171', '\122', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\156', '\103', '\161', '\040', '\141', '\156', '\040', + '\061', '\012', '\164', '\107', '\166', '\040', '\164', '\150', + '\040', '\061', '\012', '\167', '\147', '\124', '\040', '\156', + '\147', '\040', '\061', '\012', '\153', '\116', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\157', '\110', '\153', + '\040', '\157', '\156', '\040', '\061', '\012', '\127', '\172', + '\167', '\040', '\163', '\172', '\040', '\061', '\012', '\150', + '\166', '\125', '\040', '\164', '\150', '\040', '\061', '\012', + '\163', '\153', '\130', '\040', '\163', '\164', '\040', '\061', + '\012', '\166', '\131', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\152', '\157', '\132', '\040', '\157', '\156', + '\040', '\061', '\012', '\156', '\107', '\161', '\040', '\141', + '\156', '\040', '\061', '\012', '\161', '\155', '\115', '\040', + '\161', '\165', '\040', '\061', '\012', '\102', '\155', '\162', + '\040', '\145', '\162', '\040', '\061', '\012', '\163', '\126', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\165', + '\103', '\166', '\040', '\161', '\165', '\040', '\061', '\012', + '\151', '\130', '\172', '\040', '\151', '\156', '\040', '\061', + '\012', '\166', '\113', '\160', '\040', '\166', '\141', '\040', + '\061', '\012', '\154', '\105', '\167', '\040', '\154', '\145', + '\040', '\061', '\012', '\150', '\150', '\106', '\040', '\164', + '\150', '\040', '\061', '\012', '\151', '\167', '\123', '\040', + '\151', '\156', '\040', '\061', '\012', '\161', '\171', '\125', + '\040', '\161', '\165', '\040', '\061', '\012', '\152', '\152', + '\131', '\040', '\151', '\152', '\040', '\061', '\012', '\131', + '\147', '\155', '\040', '\156', '\147', '\040', '\061', '\012', + '\167', '\112', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\145', '\121', '\160', '\040', '\145', '\162', '\040', + '\061', '\012', '\131', '\146', '\142', '\040', '\142', '\145', + '\040', '\061', '\012', '\127', '\160', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\152', '\144', '\123', '\040', + '\144', '\145', '\040', '\061', '\012', '\166', '\155', '\107', + '\040', '\166', '\141', '\040', '\061', '\012', '\155', '\144', + '\124', '\040', '\144', '\145', '\040', '\061', '\012', '\147', + '\162', '\132', '\040', '\156', '\147', '\040', '\061', '\012', + '\171', '\161', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\102', '\160', '\040', '\160', '\157', '\040', + '\061', '\012', '\146', '\153', '\132', '\040', '\153', '\141', + '\040', '\061', '\012', '\161', '\145', '\102', '\040', '\161', + '\165', '\040', '\061', '\012', '\143', '\107', '\163', '\040', + '\143', '\150', '\040', '\061', '\012', '\105', '\161', '\147', + '\040', '\161', '\165', '\040', '\061', '\012', '\143', '\146', + '\117', '\040', '\143', '\150', '\040', '\061', '\012', '\165', + '\123', '\170', '\040', '\161', '\165', '\040', '\061', '\012', + '\104', '\150', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\152', '\162', '\040', '\145', '\162', '\040', + '\061', '\012', '\170', '\161', '\132', '\040', '\161', '\165', + '\040', '\061', '\012', '\171', '\121', '\146', '\040', '\156', + '\171', '\040', '\061', '\012', '\156', '\160', '\131', '\040', + '\141', '\156', '\040', '\061', '\012', '\170', '\104', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\142', '\155', + '\121', '\040', '\155', '\145', '\040', '\061', '\012', '\153', + '\115', '\142', '\040', '\153', '\141', '\040', '\061', '\012', + '\141', '\161', '\103', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\131', '\154', '\040', '\154', '\145', '\040', + '\061', '\012', '\167', '\153', '\104', '\040', '\153', '\141', + '\040', '\061', '\012', '\143', '\127', '\163', '\040', '\143', + '\150', '\040', '\061', '\012', '\171', '\171', '\112', '\040', + '\156', '\171', '\040', '\061', '\012', '\167', '\166', '\126', + '\040', '\166', '\141', '\040', '\061', '\012', '\154', '\131', + '\142', '\040', '\154', '\145', '\040', '\061', '\012', '\161', + '\162', '\127', '\040', '\161', '\165', '\040', '\061', '\012', + '\142', '\161', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\152', '\103', '\040', '\151', '\152', '\040', + '\061', '\012', '\166', '\113', '\171', '\040', '\166', '\141', + '\040', '\061', '\012', '\166', '\152', '\104', '\040', '\151', + '\152', '\040', '\061', '\012', '\163', '\104', '\163', '\040', + '\163', '\164', '\040', '\061', '\012', '\146', '\113', '\146', + '\040', '\146', '\157', '\040', '\061', '\012', '\172', '\163', + '\124', '\040', '\163', '\164', '\040', '\061', '\012', '\152', + '\131', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\131', '\167', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\110', '\152', '\167', '\040', '\151', '\152', '\040', + '\061', '\012', '\167', '\111', '\171', '\040', '\167', '\141', + '\040', '\061', '\012', '\146', '\146', '\125', '\040', '\146', + '\157', '\040', '\061', '\012', '\127', '\156', '\170', '\040', + '\141', '\156', '\040', '\061', '\012', '\145', '\110', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\146', '\127', + '\171', '\040', '\156', '\171', '\040', '\061', '\012', '\116', + '\167', '\166', '\040', '\166', '\141', '\040', '\061', '\012', + '\171', '\123', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\146', '\103', '\040', '\151', '\152', '\040', + '\061', '\012', '\170', '\130', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\147', '\162', '\111', '\040', '\156', + '\147', '\040', '\061', '\012', '\157', '\126', '\146', '\040', + '\157', '\156', '\040', '\061', '\012', '\126', '\146', '\171', + '\040', '\156', '\171', '\040', '\061', '\012', '\152', '\147', + '\131', '\040', '\156', '\147', '\040', '\061', '\012', '\110', + '\152', '\160', '\040', '\151', '\152', '\040', '\061', '\012', + '\172', '\161', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\171', '\110', '\040', '\161', '\165', '\040', + '\061', '\012', '\153', '\143', '\121', '\040', '\143', '\150', + '\040', '\061', '\012', '\172', '\163', '\105', '\040', '\163', + '\164', '\040', '\061', '\012', '\160', '\103', '\170', '\040', + '\160', '\162', '\040', '\061', '\012', '\153', '\167', '\120', + '\040', '\153', '\141', '\040', '\061', '\012', '\152', '\146', + '\121', '\040', '\151', '\152', '\040', '\061', '\012', '\167', + '\132', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\126', '\170', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\112', '\166', '\142', '\040', '\166', '\141', '\040', + '\061', '\012', '\163', '\105', '\167', '\040', '\163', '\172', + '\040', '\061', '\012', '\152', '\114', '\154', '\040', '\154', + '\145', '\040', '\061', '\012', '\144', '\117', '\170', '\040', + '\144', '\145', '\040', '\061', '\012', '\167', '\160', '\123', + '\040', '\160', '\162', '\040', '\061', '\012', '\171', '\111', + '\157', '\040', '\157', '\156', '\040', '\061', '\012', '\164', + '\107', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\166', '\110', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\107', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\147', '\166', '\121', '\040', '\156', '\147', + '\040', '\061', '\012', '\160', '\116', '\162', '\040', '\145', + '\162', '\040', '\061', '\012', '\147', '\161', '\131', '\040', + '\161', '\165', '\040', '\061', '\012', '\163', '\146', '\113', + '\040', '\163', '\164', '\040', '\061', '\012', '\144', '\131', + '\144', '\040', '\144', '\145', '\040', '\061', '\012', '\163', + '\115', '\155', '\040', '\163', '\164', '\040', '\061', '\012', + '\157', '\102', '\170', '\040', '\157', '\156', '\040', '\061', + '\012', '\161', '\163', '\106', '\040', '\161', '\165', '\040', + '\061', '\012', '\142', '\155', '\111', '\040', '\155', '\145', + '\040', '\061', '\012', '\164', '\155', '\103', '\040', '\164', + '\150', '\040', '\061', '\012', '\167', '\154', '\127', '\040', + '\154', '\145', '\040', '\061', '\012', '\124', '\167', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\163', '\162', + '\126', '\040', '\145', '\162', '\040', '\061', '\012', '\162', + '\116', '\172', '\040', '\145', '\162', '\040', '\061', '\012', + '\125', '\165', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\107', '\152', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\156', '\152', '\131', '\040', '\141', '\156', + '\040', '\061', '\012', '\166', '\117', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\121', '\155', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\106', '\156', '\146', + '\040', '\141', '\156', '\040', '\061', '\012', '\171', '\166', + '\131', '\040', '\166', '\141', '\040', '\061', '\012', '\160', + '\107', '\146', '\040', '\160', '\162', '\040', '\061', '\012', + '\154', '\110', '\160', '\040', '\141', '\154', '\040', '\061', + '\012', '\161', '\147', '\132', '\040', '\161', '\165', '\040', + '\061', '\012', '\152', '\142', '\123', '\040', '\151', '\152', + '\040', '\061', '\012', '\170', '\121', '\151', '\040', '\151', + '\156', '\040', '\061', '\012', '\164', '\161', '\107', '\040', + '\164', '\150', '\040', '\061', '\012', '\156', '\167', '\111', + '\040', '\141', '\156', '\040', '\061', '\012', '\161', '\153', + '\131', '\040', '\161', '\165', '\040', '\061', '\012', '\127', + '\170', '\171', '\040', '\156', '\171', '\040', '\061', '\012', + '\150', '\104', '\155', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\121', '\145', '\040', '\161', '\165', '\040', + '\061', '\012', '\151', '\112', '\160', '\040', '\151', '\156', + '\040', '\061', '\012', '\170', '\162', '\116', '\040', '\145', + '\162', '\040', '\061', '\012', '\144', '\107', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\153', '\121', '\170', + '\040', '\153', '\141', '\040', '\061', '\012', '\112', '\161', + '\147', '\040', '\161', '\165', '\040', '\061', '\012', '\150', + '\115', '\153', '\040', '\164', '\150', '\040', '\061', '\012', + '\154', '\152', '\124', '\040', '\154', '\145', '\040', '\061', + '\012', '\130', '\153', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\172', '\164', '\161', '\040', '\164', '\150', + '\040', '\061', '\012', '\161', '\116', '\144', '\040', '\161', + '\165', '\040', '\061', '\012', '\163', '\165', '\131', '\040', + '\161', '\165', '\040', '\061', '\012', '\125', '\157', '\141', + '\040', '\141', '\156', '\040', '\061', '\012', '\144', '\152', + '\122', '\040', '\144', '\145', '\040', '\061', '\012', '\155', + '\106', '\146', '\040', '\155', '\145', '\040', '\061', '\012', + '\152', '\172', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\152', '\122', '\040', '\163', '\172', '\040', + '\061', '\012', '\116', '\156', '\154', '\040', '\141', '\156', + '\040', '\061', '\012', '\164', '\112', '\160', '\040', '\164', + '\150', '\040', '\061', '\012', '\147', '\132', '\162', '\040', + '\156', '\147', '\040', '\061', '\012', '\102', '\167', '\170', + '\040', '\167', '\141', '\040', '\061', '\012', '\144', '\127', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\154', + '\167', '\115', '\040', '\154', '\145', '\040', '\061', '\012', + '\111', '\161', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\167', '\132', '\040', '\164', '\150', '\040', + '\061', '\012', '\115', '\167', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\153', '\152', '\131', '\040', '\151', + '\152', '\040', '\061', '\012', '\172', '\102', '\166', '\040', + '\163', '\172', '\040', '\061', '\012', '\151', '\167', '\106', + '\040', '\151', '\156', '\040', '\061', '\012', '\162', '\110', + '\172', '\040', '\145', '\162', '\040', '\061', '\012', '\123', + '\161', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\157', '\113', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\152', '\117', '\040', '\161', '\165', '\040', + '\061', '\012', '\150', '\164', '\121', '\040', '\164', '\150', + '\040', '\061', '\012', '\143', '\113', '\170', '\040', '\143', + '\150', '\040', '\061', '\012', '\142', '\161', '\127', '\040', + '\161', '\165', '\040', '\061', '\012', '\153', '\131', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\164', '\102', + '\161', '\040', '\164', '\150', '\040', '\061', '\012', '\147', + '\155', '\112', '\040', '\156', '\147', '\040', '\061', '\012', + '\145', '\131', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\150', '\107', '\166', '\040', '\164', '\150', '\040', + '\061', '\012', '\150', '\121', '\144', '\040', '\164', '\150', + '\040', '\061', '\012', '\160', '\156', '\130', '\040', '\141', + '\156', '\040', '\061', '\012', '\142', '\166', '\112', '\040', + '\166', '\141', '\040', '\061', '\012', '\163', '\170', '\115', + '\040', '\163', '\164', '\040', '\061', '\012', '\161', '\116', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\127', + '\154', '\152', '\040', '\154', '\145', '\040', '\061', '\012', + '\153', '\161', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\144', '\132', '\040', '\161', '\165', '\040', + '\061', '\012', '\155', '\150', '\131', '\040', '\164', '\150', + '\040', '\061', '\012', '\164', '\154', '\103', '\040', '\164', + '\150', '\040', '\061', '\012', '\160', '\161', '\111', '\040', + '\161', '\165', '\040', '\061', '\012', '\171', '\142', '\104', + '\040', '\142', '\145', '\040', '\061', '\012', '\170', '\101', + '\145', '\040', '\145', '\162', '\040', '\061', '\012', '\160', + '\114', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\154', '\110', '\142', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\126', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\144', '\150', '\116', '\040', '\164', '\150', + '\040', '\061', '\012', '\161', '\170', '\125', '\040', '\161', + '\165', '\040', '\061', '\012', '\144', '\126', '\146', '\040', + '\144', '\145', '\040', '\061', '\012', '\132', '\153', '\155', + '\040', '\153', '\141', '\040', '\061', '\012', '\153', '\160', + '\104', '\040', '\153', '\141', '\040', '\061', '\012', '\160', + '\152', '\110', '\040', '\151', '\152', '\040', '\061', '\012', + '\171', '\107', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\151', '\171', '\120', '\040', '\151', '\156', '\040', + '\061', '\012', '\167', '\155', '\113', '\040', '\155', '\145', + '\040', '\061', '\012', '\155', '\112', '\172', '\040', '\163', + '\172', '\040', '\061', '\012', '\146', '\155', '\114', '\040', + '\155', '\145', '\040', '\061', '\012', '\143', '\102', '\166', + '\040', '\143', '\150', '\040', '\061', '\012', '\126', '\166', + '\146', '\040', '\166', '\141', '\040', '\061', '\012', '\105', + '\161', '\154', '\040', '\161', '\165', '\040', '\061', '\012', + '\157', '\150', '\126', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\103', '\170', '\040', '\154', '\145', '\040', + '\061', '\012', '\157', '\127', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\156', '\172', '\130', '\040', '\141', + '\156', '\040', '\061', '\012', '\146', '\111', '\152', '\040', + '\151', '\152', '\040', '\061', '\012', '\153', '\120', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\160', '\131', + '\155', '\040', '\155', '\145', '\040', '\061', '\012', '\172', + '\150', '\107', '\040', '\164', '\150', '\040', '\061', '\012', + '\143', '\161', '\116', '\040', '\143', '\150', '\040', '\061', + '\012', '\165', '\155', '\121', '\040', '\161', '\165', '\040', + '\061', '\012', '\167', '\130', '\163', '\040', '\163', '\164', + '\040', '\061', '\012', '\154', '\132', '\152', '\040', '\154', + '\145', '\040', '\061', '\012', '\123', '\170', '\163', '\040', + '\163', '\164', '\040', '\061', '\012', '\113', '\161', '\144', + '\040', '\161', '\165', '\040', '\061', '\012', '\164', '\127', + '\143', '\040', '\164', '\150', '\040', '\061', '\012', '\113', + '\143', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\160', '\166', '\102', '\040', '\160', '\157', '\040', '\061', + '\012', '\164', '\147', '\122', '\040', '\164', '\150', '\040', + '\061', '\012', '\171', '\162', '\116', '\040', '\145', '\162', + '\040', '\061', '\012', '\170', '\121', '\162', '\040', '\145', + '\162', '\040', '\061', '\012', '\130', '\166', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\154', '\112', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\130', '\146', + '\153', '\040', '\153', '\141', '\040', '\061', '\012', '\106', + '\166', '\162', '\040', '\145', '\162', '\040', '\061', '\012', + '\146', '\125', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\154', '\132', '\142', '\040', '\154', '\145', '\040', + '\061', '\012', '\147', '\144', '\111', '\040', '\156', '\147', + '\040', '\061', '\012', '\152', '\157', '\111', '\040', '\157', + '\156', '\040', '\061', '\012', '\171', '\113', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\164', '\167', '\172', + '\040', '\164', '\150', '\040', '\061', '\012', '\161', '\112', + '\152', '\040', '\161', '\165', '\040', '\061', '\012', '\166', + '\170', '\115', '\040', '\166', '\141', '\040', '\061', '\012', + '\126', '\172', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\146', '\152', '\122', '\040', '\151', '\152', '\040', + '\061', '\012', '\113', '\155', '\172', '\040', '\163', '\172', + '\040', '\061', '\012', '\161', '\111', '\167', '\040', '\161', + '\165', '\040', '\061', '\012', '\152', '\171', '\104', '\040', + '\151', '\152', '\040', '\061', '\012', '\161', '\142', '\125', + '\040', '\161', '\165', '\040', '\061', '\012', '\161', '\153', + '\132', '\040', '\161', '\165', '\040', '\061', '\012', '\152', + '\126', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\106', '\150', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\112', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\167', '\120', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\125', '\145', '\157', '\040', '\145', + '\162', '\040', '\061', '\012', '\172', '\130', '\144', '\040', + '\163', '\172', '\040', '\061', '\012', '\147', '\106', '\142', + '\040', '\156', '\147', '\040', '\061', '\012', '\152', '\112', + '\171', '\040', '\151', '\152', '\040', '\061', '\012', '\116', + '\163', '\152', '\040', '\163', '\164', '\040', '\061', '\012', + '\154', '\115', '\142', '\040', '\154', '\145', '\040', '\061', + '\012', '\171', '\121', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\144', '\156', '\115', '\040', '\141', '\156', + '\040', '\061', '\012', '\171', '\122', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\106', '\152', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\144', '\113', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\147', '\161', + '\126', '\040', '\156', '\147', '\040', '\061', '\012', '\147', + '\103', '\153', '\040', '\156', '\147', '\040', '\061', '\012', + '\163', '\117', '\172', '\040', '\163', '\164', '\040', '\061', + '\012', '\150', '\154', '\117', '\040', '\164', '\150', '\040', + '\061', '\012', '\161', '\142', '\116', '\040', '\161', '\165', + '\040', '\061', '\012', '\163', '\152', '\116', '\040', '\163', + '\164', '\040', '\061', '\012', '\125', '\152', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\162', '\126', '\155', + '\040', '\145', '\162', '\040', '\061', '\012', '\127', '\152', + '\163', '\040', '\163', '\164', '\040', '\061', '\012', '\142', + '\155', '\115', '\040', '\155', '\145', '\040', '\061', '\012', + '\126', '\172', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\132', '\147', '\040', '\164', '\150', '\040', + '\061', '\012', '\172', '\106', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\171', '\150', '\112', '\040', '\164', + '\150', '\040', '\061', '\012', '\166', '\116', '\153', '\040', + '\153', '\141', '\040', '\061', '\012', '\172', '\142', '\124', + '\040', '\163', '\172', '\040', '\061', '\012', '\170', '\155', + '\112', '\040', '\155', '\145', '\040', '\061', '\012', '\106', + '\143', '\163', '\040', '\143', '\150', '\040', '\061', '\012', + '\171', '\124', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\123', '\147', '\040', '\143', '\150', '\040', + '\061', '\012', '\161', '\155', '\120', '\040', '\161', '\165', + '\040', '\061', '\012', '\155', '\106', '\172', '\040', '\163', + '\172', '\040', '\061', '\012', '\142', '\144', '\111', '\040', + '\144', '\145', '\040', '\061', '\012', '\152', '\154', '\113', + '\040', '\154', '\145', '\040', '\061', '\012', '\142', '\156', + '\102', '\040', '\141', '\156', '\040', '\061', '\012', '\161', + '\171', '\121', '\040', '\161', '\165', '\040', '\061', '\012', + '\126', '\152', '\153', '\040', '\151', '\152', '\040', '\061', + '\012', '\150', '\172', '\125', '\040', '\164', '\150', '\040', + '\061', '\012', '\161', '\147', '\160', '\040', '\161', '\165', + '\040', '\061', '\012', '\154', '\161', '\127', '\040', '\161', + '\165', '\040', '\061', '\012', '\146', '\116', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\124', '\152', '\160', + '\040', '\151', '\152', '\040', '\061', '\012', '\166', '\154', + '\126', '\040', '\154', '\145', '\040', '\061', '\012', '\162', + '\126', '\160', '\040', '\145', '\162', '\040', '\061', '\012', + '\142', '\114', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\171', '\144', '\121', '\040', '\144', '\145', '\040', + '\061', '\012', '\147', '\131', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\161', '\150', '\105', '\040', '\164', + '\150', '\040', '\061', '\012', '\107', '\163', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\147', '\127', '\172', + '\040', '\156', '\147', '\040', '\061', '\012', '\121', '\164', + '\153', '\040', '\164', '\150', '\040', '\061', '\012', '\110', + '\172', '\167', '\040', '\163', '\172', '\040', '\061', '\012', + '\153', '\111', '\157', '\040', '\150', '\157', '\040', '\061', + '\012', '\153', '\146', '\103', '\040', '\153', '\141', '\040', + '\061', '\012', '\172', '\102', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\152', '\112', '\160', '\040', '\151', + '\152', '\040', '\061', '\012', '\145', '\111', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\166', '\165', '\102', + '\040', '\161', '\165', '\040', '\061', '\012', '\127', '\142', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\112', + '\152', '\160', '\040', '\151', '\152', '\040', '\061', '\012', + '\154', '\130', '\153', '\040', '\154', '\145', '\040', '\061', + '\012', '\124', '\146', '\170', '\040', '\146', '\157', '\040', + '\061', '\012', '\172', '\114', '\154', '\040', '\154', '\145', + '\040', '\061', '\012', '\144', '\161', '\124', '\040', '\161', + '\165', '\040', '\061', '\012', '\157', '\132', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\112', '\146', '\165', + '\040', '\161', '\165', '\040', '\061', '\012', '\121', '\150', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\161', + '\153', '\113', '\040', '\161', '\165', '\040', '\061', '\012', + '\105', '\152', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\167', '\116', '\040', '\163', '\172', '\040', + '\061', '\012', '\171', '\121', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\144', '\104', '\160', '\040', '\144', + '\145', '\040', '\061', '\012', '\120', '\167', '\167', '\040', + '\167', '\141', '\040', '\061', '\012', '\172', '\164', '\103', + '\040', '\164', '\150', '\040', '\061', '\012', '\152', '\164', + '\110', '\040', '\164', '\150', '\040', '\061', '\012', '\171', + '\162', '\130', '\040', '\145', '\162', '\040', '\061', '\012', + '\166', '\167', '\124', '\040', '\166', '\141', '\040', '\061', + '\012', '\171', '\122', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\167', '\121', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\154', '\130', '\172', '\040', '\154', + '\145', '\040', '\061', '\012', '\143', '\146', '\114', '\040', + '\143', '\150', '\040', '\061', '\012', '\106', '\167', '\154', + '\040', '\154', '\145', '\040', '\061', '\012', '\162', '\116', + '\167', '\040', '\145', '\162', '\040', '\061', '\012', '\102', + '\150', '\170', '\040', '\164', '\150', '\040', '\061', '\012', + '\147', '\154', '\132', '\040', '\156', '\147', '\040', '\061', + '\012', '\147', '\143', '\104', '\040', '\143', '\150', '\040', + '\061', '\012', '\123', '\146', '\163', '\040', '\163', '\164', + '\040', '\061', '\012', '\125', '\172', '\146', '\040', '\163', + '\172', '\040', '\061', '\012', '\124', '\144', '\154', '\040', + '\154', '\145', '\040', '\061', '\012', '\144', '\122', '\156', + '\040', '\141', '\156', '\040', '\061', '\012', '\166', '\131', + '\167', '\040', '\166', '\141', '\040', '\061', '\012', '\170', + '\143', '\104', '\040', '\143', '\150', '\040', '\061', '\012', + '\170', '\143', '\103', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\102', '\170', '\040', '\154', '\145', '\040', + '\061', '\012', '\147', '\110', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\167', '\112', '\171', '\040', '\167', + '\141', '\040', '\061', '\012', '\171', '\162', '\117', '\040', + '\145', '\162', '\040', '\061', '\012', '\166', '\161', '\106', + '\040', '\161', '\165', '\040', '\061', '\012', '\164', '\131', + '\142', '\040', '\164', '\150', '\040', '\061', '\012', '\132', + '\152', '\167', '\040', '\151', '\152', '\040', '\061', '\012', + '\152', '\114', '\153', '\040', '\151', '\152', '\040', '\061', + '\012', '\110', '\166', '\146', '\040', '\166', '\141', '\040', + '\061', '\012', '\160', '\156', '\123', '\040', '\141', '\156', + '\040', '\061', '\012', '\160', '\143', '\124', '\040', '\143', + '\150', '\040', '\061', '\012', '\163', '\106', '\153', '\040', + '\163', '\164', '\040', '\061', '\012', '\144', '\143', '\117', + '\040', '\143', '\150', '\040', '\061', '\012', '\172', '\120', + '\167', '\040', '\163', '\172', '\040', '\061', '\012', '\166', + '\116', '\146', '\040', '\166', '\141', '\040', '\061', '\012', + '\107', '\144', '\170', '\040', '\144', '\145', '\040', '\061', + '\012', '\144', '\154', '\120', '\040', '\154', '\145', '\040', + '\061', '\012', '\152', '\114', '\170', '\040', '\152', '\157', + '\040', '\061', '\012', '\152', '\132', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\167', '\167', '\124', '\040', + '\167', '\141', '\040', '\061', '\012', '\164', '\107', '\170', + '\040', '\164', '\150', '\040', '\061', '\012', '\146', '\150', + '\123', '\040', '\164', '\150', '\040', '\061', '\012', '\130', + '\164', '\153', '\040', '\164', '\150', '\040', '\061', '\012', + '\170', '\156', '\127', '\040', '\157', '\156', '\040', '\061', + '\012', '\160', '\153', '\112', '\040', '\153', '\141', '\040', + '\061', '\012', '\170', '\111', '\157', '\040', '\157', '\156', + '\040', '\061', '\012', '\132', '\170', '\142', '\040', '\142', + '\145', '\040', '\061', '\012', '\156', '\117', '\152', '\040', + '\141', '\156', '\040', '\061', '\012', '\167', '\110', '\152', + '\040', '\151', '\152', '\040', '\061', '\012', '\146', '\152', + '\123', '\040', '\151', '\152', '\040', '\061', '\012', '\167', + '\144', '\114', '\040', '\144', '\145', '\040', '\061', '\012', + '\152', '\142', '\116', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\153', '\117', '\040', '\153', '\141', '\040', + '\061', '\012', '\170', '\161', '\102', '\040', '\161', '\165', + '\040', '\061', '\012', '\161', '\172', '\116', '\040', '\161', + '\165', '\040', '\061', '\012', '\121', '\142', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\106', '\161', '\167', + '\040', '\161', '\165', '\040', '\061', '\012', '\152', '\127', + '\167', '\040', '\151', '\152', '\040', '\061', '\012', '\156', + '\170', '\115', '\040', '\141', '\156', '\040', '\061', '\012', + '\164', '\160', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\124', '\164', '\172', '\040', '\164', '\150', '\040', + '\061', '\012', '\172', '\163', '\110', '\040', '\163', '\164', + '\040', '\061', '\012', '\146', '\152', '\172', '\040', '\163', + '\172', '\040', '\061', '\012', '\170', '\111', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\170', '\153', '\131', + '\040', '\153', '\141', '\040', '\061', '\012', '\106', '\161', + '\141', '\040', '\141', '\156', '\040', '\061', '\012', '\157', + '\107', '\153', '\040', '\157', '\156', '\040', '\061', '\012', + '\110', '\156', '\143', '\040', '\141', '\156', '\040', '\061', + '\012', '\152', '\120', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\172', '\154', '\127', '\040', '\154', '\145', + '\040', '\061', '\012', '\165', '\122', '\170', '\040', '\161', + '\165', '\040', '\061', '\012', '\165', '\107', '\170', '\040', + '\161', '\165', '\040', '\061', '\012', '\152', '\131', '\166', + '\040', '\151', '\152', '\040', '\061', '\012', '\113', '\160', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\147', + '\121', '\157', '\040', '\156', '\147', '\040', '\061', '\012', + '\113', '\167', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\152', '\116', '\167', '\040', '\151', '\152', '\040', + '\061', '\012', '\164', '\144', '\104', '\040', '\164', '\150', + '\040', '\061', '\012', '\171', '\107', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\114', '\142', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\122', '\162', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\161', '\166', + '\130', '\040', '\161', '\165', '\040', '\061', '\012', '\150', + '\150', '\113', '\040', '\164', '\150', '\040', '\061', '\012', + '\153', '\132', '\170', '\040', '\153', '\141', '\040', '\061', + '\012', '\170', '\104', '\146', '\040', '\146', '\157', '\040', + '\061', '\012', '\120', '\152', '\146', '\040', '\151', '\152', + '\040', '\061', '\012', '\143', '\147', '\106', '\040', '\143', + '\150', '\040', '\061', '\012', '\166', '\103', '\153', '\040', + '\153', '\141', '\040', '\061', '\012', '\146', '\127', '\167', + '\040', '\157', '\167', '\040', '\061', '\012', '\155', '\112', + '\160', '\040', '\155', '\145', '\040', '\061', '\012', '\146', + '\130', '\145', '\040', '\145', '\162', '\040', '\061', '\012', + '\165', '\131', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\110', '\153', '\040', '\151', '\152', '\040', + '\061', '\012', '\167', '\144', '\120', '\040', '\144', '\145', + '\040', '\061', '\012', '\161', '\106', '\153', '\040', '\161', + '\165', '\040', '\061', '\012', '\152', '\162', '\107', '\040', + '\145', '\162', '\040', '\061', '\012', '\146', '\147', '\104', + '\040', '\156', '\147', '\040', '\061', '\012', '\146', '\163', + '\107', '\040', '\163', '\164', '\040', '\061', '\012', '\126', + '\147', '\142', '\040', '\156', '\147', '\040', '\061', '\012', + '\170', '\101', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\164', '\132', '\040', '\164', '\150', '\040', + '\061', '\012', '\164', '\154', '\161', '\040', '\164', '\150', + '\040', '\061', '\012', '\124', '\155', '\167', '\040', '\155', + '\145', '\040', '\061', '\012', '\147', '\171', '\131', '\040', + '\156', '\147', '\040', '\061', '\012', '\121', '\170', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\125', '\170', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\151', + '\126', '\162', '\040', '\151', '\156', '\040', '\061', '\012', + '\172', '\161', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\116', '\142', '\167', '\040', '\167', '\141', '\040', + '\061', '\012', '\104', '\150', '\144', '\040', '\164', '\150', + '\040', '\061', '\012', '\155', '\117', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\151', '\102', '\144', '\040', + '\151', '\156', '\040', '\061', '\012', '\143', '\161', '\102', + '\040', '\143', '\150', '\040', '\061', '\012', '\172', '\121', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\127', + '\142', '\166', '\040', '\166', '\141', '\040', '\061', '\012', + '\121', '\153', '\163', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\120', '\141', '\040', '\141', '\156', '\040', + '\061', '\012', '\164', '\146', '\111', '\040', '\164', '\150', + '\040', '\061', '\012', '\155', '\132', '\163', '\040', '\163', + '\164', '\040', '\061', '\012', '\160', '\104', '\163', '\040', + '\163', '\164', '\040', '\061', '\012', '\156', '\112', '\152', + '\040', '\141', '\156', '\040', '\061', '\012', '\172', '\143', + '\160', '\040', '\143', '\150', '\040', '\061', '\012', '\164', + '\127', '\152', '\040', '\164', '\150', '\040', '\061', '\012', + '\132', '\170', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\166', '\120', '\171', '\040', '\166', '\141', '\040', + '\061', '\012', '\144', '\170', '\113', '\040', '\144', '\145', + '\040', '\061', '\012', '\157', '\120', '\166', '\040', '\157', + '\156', '\040', '\061', '\012', '\162', '\152', '\116', '\040', + '\145', '\162', '\040', '\061', '\012', '\157', '\121', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\166', '\167', + '\110', '\040', '\166', '\141', '\040', '\061', '\012', '\121', + '\150', '\160', '\040', '\164', '\150', '\040', '\061', '\012', + '\170', '\163', '\125', '\040', '\163', '\164', '\040', '\061', + '\012', '\153', '\107', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\167', '\152', '\127', '\040', '\151', '\152', + '\040', '\061', '\012', '\120', '\167', '\170', '\040', '\167', + '\141', '\040', '\061', '\012', '\102', '\142', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\170', '\117', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\161', '\160', + '\116', '\040', '\161', '\165', '\040', '\061', '\012', '\156', + '\142', '\161', '\040', '\141', '\156', '\040', '\061', '\012', + '\172', '\160', '\115', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\155', '\102', '\040', '\151', '\152', '\040', + '\061', '\012', '\116', '\161', '\152', '\040', '\161', '\165', + '\040', '\061', '\012', '\172', '\131', '\144', '\040', '\163', + '\172', '\040', '\061', '\012', '\131', '\142', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\170', '\143', '\127', + '\040', '\143', '\150', '\040', '\061', '\012', '\147', '\120', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\121', + '\171', '\163', '\040', '\163', '\164', '\040', '\061', '\012', + '\102', '\150', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\107', '\170', '\040', '\156', '\171', '\040', + '\061', '\012', '\161', '\170', '\114', '\040', '\161', '\165', + '\040', '\061', '\012', '\112', '\146', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\155', '\142', '\126', '\040', + '\155', '\145', '\040', '\061', '\012', '\160', '\153', '\131', + '\040', '\153', '\141', '\040', '\061', '\012', '\143', '\127', + '\154', '\040', '\143', '\150', '\040', '\061', '\012', '\167', + '\102', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\166', '\117', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\107', '\160', '\142', '\040', '\160', '\162', '\040', + '\061', '\012', '\120', '\160', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\146', '\163', '\130', '\040', '\163', + '\164', '\040', '\061', '\012', '\166', '\164', '\121', '\040', + '\164', '\150', '\040', '\061', '\012', '\171', '\103', '\152', + '\040', '\151', '\152', '\040', '\061', '\012', '\171', '\157', + '\131', '\040', '\157', '\156', '\040', '\061', '\012', '\160', + '\167', '\121', '\040', '\160', '\162', '\040', '\061', '\012', + '\171', '\107', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\164', '\112', '\040', '\164', '\150', '\040', + '\061', '\012', '\156', '\162', '\132', '\040', '\141', '\156', + '\040', '\061', '\012', '\145', '\126', '\170', '\040', '\145', + '\162', '\040', '\061', '\012', '\116', '\162', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\167', '\164', '\101', + '\040', '\164', '\150', '\040', '\061', '\012', '\146', '\110', + '\146', '\040', '\146', '\157', '\040', '\061', '\012', '\147', + '\163', '\121', '\040', '\156', '\147', '\040', '\061', '\012', + '\150', '\154', '\103', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\114', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\172', '\152', '\103', '\040', '\163', '\172', + '\040', '\061', '\012', '\152', '\166', '\131', '\040', '\151', + '\152', '\040', '\061', '\012', '\164', '\111', '\152', '\040', + '\164', '\150', '\040', '\061', '\012', '\160', '\166', '\114', + '\040', '\166', '\141', '\040', '\061', '\012', '\110', '\150', + '\147', '\040', '\164', '\150', '\040', '\061', '\012', '\171', + '\115', '\166', '\040', '\166', '\141', '\040', '\061', '\012', + '\170', '\115', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\131', '\170', '\040', '\164', '\150', '\040', + '\061', '\012', '\166', '\126', '\160', '\040', '\166', '\141', + '\040', '\061', '\012', '\131', '\156', '\142', '\040', '\141', + '\156', '\040', '\061', '\012', '\166', '\155', '\130', '\040', + '\166', '\141', '\040', '\061', '\012', '\161', '\152', '\121', + '\040', '\161', '\165', '\040', '\061', '\012', '\166', '\121', + '\162', '\040', '\145', '\162', '\040', '\061', '\012', '\150', + '\121', '\172', '\040', '\164', '\150', '\040', '\061', '\012', + '\155', '\116', '\146', '\040', '\155', '\145', '\040', '\061', + '\012', '\172', '\146', '\131', '\040', '\163', '\172', '\040', + '\061', '\012', '\170', '\152', '\123', '\040', '\151', '\152', + '\040', '\061', '\012', '\152', '\102', '\155', '\040', '\151', + '\152', '\040', '\061', '\012', '\152', '\160', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\156', '\112', '\161', + '\040', '\141', '\156', '\040', '\061', '\012', '\113', '\156', + '\172', '\040', '\141', '\156', '\040', '\061', '\012', '\147', + '\107', '\146', '\040', '\156', '\147', '\040', '\061', '\012', + '\160', '\132', '\170', '\040', '\160', '\162', '\040', '\061', + '\012', '\107', '\161', '\154', '\040', '\161', '\165', '\040', + '\061', '\012', '\125', '\161', '\155', '\040', '\161', '\165', + '\040', '\061', '\012', '\145', '\127', '\166', '\040', '\145', + '\162', '\040', '\061', '\012', '\146', '\107', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\161', '\163', '\101', + '\040', '\161', '\165', '\040', '\061', '\012', '\165', '\150', + '\131', '\040', '\164', '\150', '\040', '\061', '\012', '\170', + '\150', '\110', '\040', '\164', '\150', '\040', '\061', '\012', + '\171', '\170', '\123', '\040', '\156', '\171', '\040', '\061', + '\012', '\162', '\170', '\113', '\040', '\145', '\162', '\040', + '\061', '\012', '\150', '\116', '\143', '\040', '\164', '\150', + '\040', '\061', '\012', '\126', '\167', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\141', '\116', '\166', '\040', + '\141', '\156', '\040', '\061', '\012', '\121', '\172', '\166', + '\040', '\163', '\172', '\040', '\061', '\012', '\146', '\121', + '\156', '\040', '\141', '\156', '\040', '\061', '\012', '\152', + '\172', '\110', '\040', '\163', '\172', '\040', '\061', '\012', + '\122', '\166', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\160', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\161', '\130', '\166', '\040', '\161', '\165', + '\040', '\061', '\012', '\160', '\150', '\121', '\040', '\164', + '\150', '\040', '\061', '\012', '\121', '\154', '\142', '\040', + '\154', '\145', '\040', '\061', '\012', '\142', '\156', '\121', + '\040', '\141', '\156', '\040', '\061', '\012', '\156', '\152', + '\113', '\040', '\141', '\156', '\040', '\061', '\012', '\112', + '\152', '\163', '\040', '\163', '\164', '\040', '\061', '\012', + '\164', '\112', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\167', '\130', '\040', '\151', '\156', '\040', + '\061', '\012', '\156', '\126', '\144', '\040', '\141', '\156', + '\040', '\061', '\012', '\153', '\172', '\101', '\040', '\163', + '\172', '\040', '\061', '\012', '\165', '\167', '\105', '\040', + '\161', '\165', '\040', '\061', '\012', '\124', '\163', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\150', '\161', + '\115', '\040', '\164', '\150', '\040', '\061', '\012', '\122', + '\156', '\161', '\040', '\141', '\156', '\040', '\061', '\012', + '\162', '\104', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\116', '\142', '\040', '\142', '\145', '\040', + '\061', '\012', '\165', '\161', '\116', '\040', '\161', '\165', + '\040', '\061', '\012', '\146', '\113', '\167', '\040', '\167', + '\141', '\040', '\061', '\012', '\111', '\161', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\170', '\110', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\127', '\167', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\147', + '\115', '\167', '\040', '\156', '\147', '\040', '\061', '\012', + '\171', '\127', '\146', '\040', '\156', '\171', '\040', '\061', + '\012', '\166', '\143', '\117', '\040', '\143', '\150', '\040', + '\061', '\012', '\107', '\153', '\155', '\040', '\153', '\141', + '\040', '\061', '\012', '\146', '\122', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\144', '\115', '\143', '\040', + '\156', '\144', '\040', '\061', '\012', '\132', '\150', '\170', + '\040', '\164', '\150', '\040', '\061', '\012', '\161', '\154', + '\110', '\040', '\161', '\165', '\040', '\061', '\012', '\161', + '\125', '\154', '\040', '\161', '\165', '\040', '\061', '\012', + '\172', '\110', '\146', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\103', '\153', '\040', '\153', '\141', '\040', + '\061', '\012', '\121', '\146', '\152', '\040', '\151', '\152', + '\040', '\061', '\012', '\121', '\153', '\167', '\040', '\153', + '\141', '\040', '\061', '\012', '\155', '\131', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\144', '\143', '\125', + '\040', '\143', '\150', '\040', '\061', '\012', '\152', '\124', + '\146', '\040', '\151', '\152', '\040', '\061', '\012', '\162', + '\152', '\106', '\040', '\145', '\162', '\040', '\061', '\012', + '\150', '\170', '\121', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\116', '\146', '\040', '\167', '\141', '\040', + '\061', '\012', '\114', '\147', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\106', '\144', '\165', '\040', '\161', + '\165', '\040', '\061', '\012', '\164', '\112', '\167', '\040', + '\164', '\150', '\040', '\061', '\012', '\171', '\143', '\121', + '\040', '\143', '\150', '\040', '\061', '\012', '\170', '\130', + '\146', '\040', '\146', '\157', '\040', '\061', '\012', '\167', + '\167', '\121', '\040', '\167', '\141', '\040', '\061', '\012', + '\145', '\166', '\121', '\040', '\145', '\162', '\040', '\061', + '\012', '\106', '\143', '\152', '\040', '\143', '\150', '\040', + '\061', '\012', '\103', '\171', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\164', '\160', '\106', '\040', '\164', + '\150', '\040', '\061', '\012', '\101', '\170', '\152', '\040', + '\151', '\152', '\040', '\061', '\012', '\172', '\107', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\121', '\142', + '\142', '\040', '\142', '\145', '\040', '\061', '\012', '\166', + '\146', '\131', '\040', '\166', '\141', '\040', '\061', '\012', + '\157', '\130', '\144', '\040', '\157', '\156', '\040', '\061', + '\012', '\167', '\101', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\130', '\142', '\153', '\040', '\153', '\141', + '\040', '\061', '\012', '\167', '\155', '\122', '\040', '\155', + '\145', '\040', '\061', '\012', '\162', '\172', '\116', '\040', + '\145', '\162', '\040', '\061', '\012', '\146', '\143', '\102', + '\040', '\143', '\150', '\040', '\061', '\012', '\102', '\167', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\170', + '\147', '\123', '\040', '\156', '\147', '\040', '\061', '\012', + '\144', '\121', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\153', '\112', '\167', '\040', '\153', '\141', '\040', + '\061', '\012', '\142', '\147', '\170', '\040', '\156', '\147', + '\040', '\061', '\012', '\160', '\132', '\163', '\040', '\163', + '\172', '\040', '\061', '\012', '\167', '\146', '\101', '\040', + '\167', '\141', '\040', '\061', '\012', '\152', '\155', '\130', + '\040', '\151', '\152', '\040', '\061', '\012', '\144', '\116', + '\160', '\040', '\144', '\145', '\040', '\061', '\012', '\126', + '\170', '\162', '\040', '\145', '\162', '\040', '\061', '\012', + '\122', '\166', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\132', '\154', '\040', '\154', '\145', '\040', + '\061', '\012', '\167', '\147', '\101', '\040', '\156', '\147', + '\040', '\061', '\012', '\127', '\162', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\112', '\143', '\161', '\040', + '\143', '\150', '\040', '\061', '\012', '\154', '\152', '\127', + '\040', '\154', '\145', '\040', '\061', '\012', '\161', '\120', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\147', + '\152', '\131', '\040', '\156', '\147', '\040', '\061', '\012', + '\152', '\125', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\155', '\111', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\110', '\160', '\171', '\040', '\160', '\162', + '\040', '\061', '\012', '\115', '\160', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\142', '\153', '\117', '\040', + '\153', '\141', '\040', '\061', '\012', '\101', '\166', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\166', '\113', + '\153', '\040', '\153', '\141', '\040', '\061', '\012', '\102', + '\146', '\172', '\040', '\163', '\172', '\040', '\061', '\012', + '\171', '\131', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\105', '\147', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\167', '\170', '\110', '\040', '\167', '\141', + '\040', '\061', '\012', '\172', '\110', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\163', '\166', '\101', '\040', + '\163', '\164', '\040', '\061', '\012', '\172', '\143', '\120', + '\040', '\143', '\150', '\040', '\061', '\012', '\102', '\170', + '\157', '\040', '\157', '\156', '\040', '\061', '\012', '\150', + '\123', '\166', '\040', '\164', '\150', '\040', '\061', '\012', + '\114', '\170', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\102', '\172', '\040', '\164', '\150', '\040', + '\061', '\012', '\143', '\127', '\153', '\040', '\143', '\150', + '\040', '\061', '\012', '\170', '\102', '\166', '\040', '\166', + '\141', '\040', '\061', '\012', '\150', '\167', '\116', '\040', + '\164', '\150', '\040', '\061', '\012', '\155', '\153', '\112', + '\040', '\153', '\141', '\040', '\061', '\012', '\157', '\116', + '\152', '\040', '\157', '\156', '\040', '\061', '\012', '\125', + '\147', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\152', '\132', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\146', '\120', '\040', '\146', '\157', '\040', + '\061', '\012', '\142', '\131', '\166', '\040', '\166', '\141', + '\040', '\061', '\012', '\161', '\170', '\106', '\040', '\161', + '\165', '\040', '\061', '\012', '\144', '\143', '\111', '\040', + '\143', '\150', '\040', '\061', '\012', '\144', '\150', '\131', + '\040', '\164', '\150', '\040', '\061', '\012', '\143', '\166', + '\120', '\040', '\143', '\150', '\040', '\061', '\012', '\161', + '\125', '\171', '\040', '\161', '\165', '\040', '\061', '\012', + '\155', '\170', '\103', '\040', '\155', '\145', '\040', '\061', + '\012', '\172', '\120', '\170', '\040', '\163', '\172', '\040', + '\061', '\012', '\116', '\161', '\154', '\040', '\161', '\165', + '\040', '\061', '\012', '\131', '\146', '\167', '\040', '\167', + '\141', '\040', '\061', '\012', '\127', '\147', '\160', '\040', + '\156', '\147', '\040', '\061', '\012', '\152', '\147', '\104', + '\040', '\156', '\147', '\040', '\061', '\012', '\121', '\146', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\154', + '\143', '\127', '\040', '\143', '\150', '\040', '\061', '\012', + '\161', '\170', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\130', '\160', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\167', '\162', '\104', '\040', '\145', '\162', + '\040', '\061', '\012', '\142', '\105', '\157', '\040', '\157', + '\156', '\040', '\061', '\012', '\142', '\172', '\126', '\040', + '\163', '\172', '\040', '\061', '\012', '\146', '\167', '\123', + '\040', '\167', '\141', '\040', '\061', '\012', '\155', '\114', + '\152', '\040', '\151', '\152', '\040', '\061', '\012', '\167', + '\115', '\162', '\040', '\145', '\162', '\040', '\061', '\012', + '\166', '\106', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\146', '\124', '\040', '\163', '\172', '\040', + '\061', '\012', '\156', '\122', '\153', '\040', '\141', '\156', + '\040', '\061', '\012', '\153', '\112', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\122', '\155', '\167', '\040', + '\155', '\145', '\040', '\061', '\012', '\156', '\161', '\122', + '\040', '\141', '\156', '\040', '\061', '\012', '\161', '\160', + '\117', '\040', '\161', '\165', '\040', '\061', '\012', '\142', + '\110', '\142', '\040', '\142', '\145', '\040', '\061', '\012', + '\124', '\153', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\152', '\107', '\040', '\163', '\164', '\040', + '\061', '\012', '\161', '\141', '\124', '\040', '\141', '\156', + '\040', '\061', '\012', '\120', '\161', '\154', '\040', '\161', + '\165', '\040', '\061', '\012', '\150', '\154', '\121', '\040', + '\164', '\150', '\040', '\061', '\012', '\153', '\172', '\127', + '\040', '\163', '\172', '\040', '\061', '\012', '\171', '\106', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\165', + '\102', '\166', '\040', '\161', '\165', '\040', '\061', '\012', + '\166', '\170', '\117', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\166', '\103', '\040', '\161', '\165', '\040', + '\061', '\012', '\131', '\161', '\170', '\040', '\161', '\165', + '\040', '\061', '\012', '\152', '\103', '\142', '\040', '\151', + '\152', '\040', '\061', '\012', '\121', '\152', '\153', '\040', + '\151', '\152', '\040', '\061', '\012', '\146', '\102', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\166', '\113', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\162', + '\115', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\150', '\122', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\153', '\125', '\040', '\153', '\141', '\040', + '\061', '\012', '\142', '\125', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\166', '\131', '\166', '\040', '\166', + '\141', '\040', '\061', '\012', '\120', '\144', '\170', '\040', + '\144', '\145', '\040', '\061', '\012', '\157', '\107', '\166', + '\040', '\157', '\156', '\040', '\061', '\012', '\152', '\114', + '\171', '\040', '\151', '\152', '\040', '\061', '\012', '\144', + '\165', '\131', '\040', '\161', '\165', '\040', '\061', '\012', + '\127', '\143', '\160', '\040', '\143', '\150', '\040', '\061', + '\012', '\157', '\107', '\170', '\040', '\157', '\156', '\040', + '\061', '\012', '\166', '\107', '\154', '\040', '\154', '\145', + '\040', '\061', '\012', '\112', '\144', '\172', '\040', '\163', + '\172', '\040', '\061', '\012', '\151', '\152', '\110', '\040', + '\151', '\156', '\040', '\061', '\012', '\155', '\154', '\130', + '\040', '\154', '\145', '\040', '\061', '\012', '\152', '\116', + '\162', '\040', '\145', '\162', '\040', '\061', '\012', '\153', + '\103', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\107', '\150', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\115', '\166', '\040', '\145', '\162', '\040', + '\061', '\012', '\102', '\147', '\160', '\040', '\156', '\147', + '\040', '\061', '\012', '\142', '\106', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\165', '\127', '\154', '\040', + '\161', '\165', '\040', '\061', '\012', '\144', '\130', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\127', '\143', + '\146', '\040', '\143', '\150', '\040', '\061', '\012', '\144', + '\142', '\111', '\040', '\144', '\145', '\040', '\061', '\012', + '\142', '\107', '\170', '\040', '\142', '\145', '\040', '\061', + '\012', '\145', '\170', '\121', '\040', '\145', '\162', '\040', + '\061', '\012', '\152', '\127', '\152', '\040', '\152', '\157', + '\040', '\061', '\012', '\160', '\121', '\142', '\040', '\160', + '\162', '\040', '\061', '\012', '\152', '\143', '\110', '\040', + '\143', '\150', '\040', '\061', '\012', '\161', '\117', '\154', + '\040', '\161', '\165', '\040', '\061', '\012', '\155', '\164', + '\114', '\040', '\164', '\150', '\040', '\061', '\012', '\143', + '\162', '\103', '\040', '\143', '\150', '\040', '\061', '\012', + '\160', '\102', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\127', '\154', '\172', '\040', '\154', '\145', '\040', + '\061', '\012', '\156', '\110', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\110', '\146', '\160', '\040', '\160', + '\162', '\040', '\061', '\012', '\130', '\160', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\125', '\170', '\160', + '\040', '\160', '\162', '\040', '\061', '\012', '\113', '\163', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\170', + '\127', '\153', '\040', '\153', '\141', '\040', '\061', '\012', + '\156', '\161', '\132', '\040', '\141', '\156', '\040', '\061', + '\012', '\103', '\170', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\172', '\112', '\170', '\040', '\163', '\172', + '\040', '\061', '\012', '\162', '\127', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\103', '\142', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\161', '\161', '\120', + '\040', '\161', '\165', '\040', '\061', '\012', '\154', '\150', + '\125', '\040', '\164', '\150', '\040', '\061', '\012', '\125', + '\146', '\166', '\040', '\166', '\141', '\040', '\061', '\012', + '\125', '\170', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\112', '\146', '\040', '\164', '\150', '\040', + '\061', '\012', '\156', '\166', '\121', '\040', '\141', '\156', + '\040', '\061', '\012', '\144', '\150', '\106', '\040', '\164', + '\150', '\040', '\061', '\012', '\103', '\166', '\142', '\040', + '\166', '\141', '\040', '\061', '\012', '\141', '\120', '\146', + '\040', '\141', '\156', '\040', '\061', '\012', '\112', '\170', + '\152', '\040', '\151', '\152', '\040', '\061', '\012', '\104', + '\167', '\160', '\040', '\160', '\162', '\040', '\061', '\012', + '\111', '\170', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\153', '\146', '\123', '\040', '\153', '\141', '\040', + '\061', '\012', '\162', '\132', '\155', '\040', '\145', '\162', + '\040', '\061', '\012', '\146', '\155', '\105', '\040', '\155', + '\145', '\040', '\061', '\012', '\163', '\114', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\142', '\155', '\122', + '\040', '\155', '\145', '\040', '\061', '\012', '\165', '\103', + '\163', '\040', '\161', '\165', '\040', '\061', '\012', '\153', + '\106', '\155', '\040', '\153', '\141', '\040', '\061', '\012', + '\113', '\161', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\121', '\153', '\040', '\153', '\141', '\040', + '\061', '\012', '\123', '\146', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\146', '\147', '\125', '\040', '\156', + '\147', '\040', '\061', '\012', '\166', '\166', '\124', '\040', + '\166', '\141', '\040', '\061', '\012', '\155', '\121', '\145', + '\040', '\145', '\162', '\040', '\061', '\012', '\107', '\142', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\164', + '\142', '\131', '\040', '\164', '\150', '\040', '\061', '\012', + '\154', '\121', '\153', '\040', '\154', '\145', '\040', '\061', + '\012', '\143', '\111', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\124', '\152', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\156', '\121', '\147', '\040', '\141', + '\156', '\040', '\061', '\012', '\171', '\131', '\160', '\040', + '\160', '\162', '\040', '\061', '\012', '\161', '\120', '\167', + '\040', '\161', '\165', '\040', '\061', '\012', '\170', '\117', + '\141', '\040', '\141', '\156', '\040', '\061', '\012', '\160', + '\116', '\167', '\040', '\160', '\162', '\040', '\061', '\012', + '\146', '\112', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\110', '\142', '\040', '\163', '\172', '\040', + '\061', '\012', '\153', '\102', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\146', '\144', '\105', '\040', '\144', + '\145', '\040', '\061', '\012', '\167', '\120', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\154', '\126', '\166', + '\040', '\154', '\145', '\040', '\061', '\012', '\155', '\120', + '\167', '\040', '\155', '\145', '\040', '\061', '\012', '\122', + '\155', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\170', '\157', '\105', '\040', '\157', '\156', '\040', '\061', + '\012', '\150', '\156', '\112', '\040', '\164', '\150', '\040', + '\061', '\012', '\165', '\166', '\105', '\040', '\161', '\165', + '\040', '\061', '\012', '\127', '\157', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\165', '\143', '\130', '\040', + '\143', '\150', '\040', '\061', '\012', '\156', '\155', '\104', + '\040', '\141', '\156', '\040', '\061', '\012', '\160', '\143', + '\130', '\040', '\143', '\150', '\040', '\061', '\012', '\150', + '\104', '\167', '\040', '\164', '\150', '\040', '\061', '\012', + '\144', '\147', '\111', '\040', '\156', '\147', '\040', '\061', + '\012', '\166', '\126', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\164', '\104', '\150', '\040', '\143', '\150', + '\040', '\061', '\012', '\152', '\110', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\150', '\153', '\130', '\040', + '\164', '\150', '\040', '\061', '\012', '\160', '\170', '\124', + '\040', '\160', '\162', '\040', '\061', '\012', '\170', '\131', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\162', + '\124', '\160', '\040', '\145', '\162', '\040', '\061', '\012', + '\125', '\142', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\114', '\154', '\155', '\040', '\154', '\145', '\040', + '\061', '\012', '\171', '\152', '\132', '\040', '\151', '\152', + '\040', '\061', '\012', '\121', '\163', '\163', '\040', '\163', + '\164', '\040', '\061', '\012', '\143', '\146', '\115', '\040', + '\143', '\150', '\040', '\061', '\012', '\152', '\142', '\107', + '\040', '\142', '\145', '\040', '\061', '\012', '\112', '\146', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\155', + '\127', '\142', '\040', '\155', '\145', '\040', '\061', '\012', + '\152', '\104', '\160', '\040', '\151', '\152', '\040', '\061', + '\012', '\154', '\127', '\172', '\040', '\154', '\145', '\040', + '\061', '\012', '\143', '\130', '\171', '\040', '\143', '\150', + '\040', '\061', '\012', '\157', '\121', '\162', '\040', '\145', + '\162', '\040', '\061', '\012', '\165', '\143', '\132', '\040', + '\143', '\150', '\040', '\061', '\012', '\143', '\166', '\116', + '\040', '\143', '\150', '\040', '\061', '\012', '\143', '\166', + '\113', '\040', '\143', '\150', '\040', '\061', '\012', '\172', + '\104', '\153', '\040', '\163', '\172', '\040', '\061', '\012', + '\142', '\114', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\144', '\104', '\154', '\040', '\154', '\145', '\040', + '\061', '\012', '\150', '\150', '\104', '\040', '\164', '\150', + '\040', '\061', '\012', '\166', '\155', '\113', '\040', '\166', + '\141', '\040', '\061', '\012', '\150', '\114', '\164', '\040', + '\164', '\150', '\040', '\061', '\012', '\155', '\161', '\127', + '\040', '\161', '\165', '\040', '\061', '\012', '\102', '\146', + '\163', '\040', '\163', '\164', '\040', '\061', '\012', '\101', + '\143', '\152', '\040', '\143', '\150', '\040', '\061', '\012', + '\144', '\143', '\107', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\112', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\155', '\146', '\123', '\040', '\155', '\145', + '\040', '\061', '\012', '\144', '\162', '\114', '\040', '\145', + '\162', '\040', '\061', '\012', '\161', '\171', '\113', '\040', + '\161', '\165', '\040', '\061', '\012', '\164', '\121', '\172', + '\040', '\164', '\150', '\040', '\061', '\012', '\152', '\162', + '\114', '\040', '\145', '\162', '\040', '\061', '\012', '\143', + '\143', '\112', '\040', '\143', '\150', '\040', '\061', '\012', + '\167', '\160', '\130', '\040', '\160', '\162', '\040', '\061', + '\012', '\132', '\172', '\146', '\040', '\163', '\172', '\040', + '\061', '\012', '\163', '\156', '\125', '\040', '\141', '\156', + '\040', '\061', '\012', '\161', '\105', '\167', '\040', '\161', + '\165', '\040', '\061', '\012', '\164', '\121', '\142', '\040', + '\164', '\150', '\040', '\061', '\012', '\155', '\120', '\144', + '\040', '\144', '\145', '\040', '\061', '\012', '\166', '\112', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\166', + '\160', '\125', '\040', '\166', '\141', '\040', '\061', '\012', + '\166', '\172', '\115', '\040', '\163', '\172', '\040', '\061', + '\012', '\165', '\132', '\142', '\040', '\161', '\165', '\040', + '\061', '\012', '\171', '\167', '\125', '\040', '\167', '\141', + '\040', '\061', '\012', '\122', '\152', '\163', '\040', '\163', + '\164', '\040', '\061', '\012', '\150', '\113', '\164', '\040', + '\164', '\150', '\040', '\061', '\012', '\102', '\146', '\142', + '\040', '\142', '\145', '\040', '\061', '\012', '\167', '\165', + '\121', '\040', '\161', '\165', '\040', '\061', '\012', '\142', + '\166', '\115', '\040', '\166', '\141', '\040', '\061', '\012', + '\171', '\151', '\127', '\040', '\151', '\156', '\040', '\061', + '\012', '\150', '\161', '\103', '\040', '\164', '\150', '\040', + '\061', '\012', '\151', '\125', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\154', '\102', '\144', '\040', '\154', + '\145', '\040', '\061', '\012', '\132', '\170', '\152', '\040', + '\151', '\152', '\040', '\061', '\012', '\167', '\160', '\127', + '\040', '\160', '\162', '\040', '\061', '\012', '\162', '\110', + '\155', '\040', '\145', '\162', '\040', '\061', '\012', '\155', + '\150', '\121', '\040', '\164', '\150', '\040', '\061', '\012', + '\146', '\115', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\166', '\127', '\146', '\040', '\166', '\141', '\040', + '\061', '\012', '\106', '\144', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\152', '\107', '\142', '\040', '\151', + '\152', '\040', '\061', '\012', '\104', '\150', '\167', '\040', + '\164', '\150', '\040', '\061', '\012', '\143', '\152', '\122', + '\040', '\143', '\150', '\040', '\061', '\012', '\153', '\166', + '\104', '\040', '\153', '\141', '\040', '\061', '\012', '\161', + '\166', '\104', '\040', '\161', '\165', '\040', '\061', '\012', + '\130', '\155', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\103', '\152', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\153', '\153', '\130', '\040', '\153', '\141', + '\040', '\061', '\012', '\161', '\153', '\106', '\040', '\161', + '\165', '\040', '\061', '\012', '\166', '\127', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\115', '\163', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\156', '\116', + '\166', '\040', '\141', '\156', '\040', '\061', '\012', '\110', + '\172', '\165', '\040', '\161', '\165', '\040', '\061', '\012', + '\172', '\162', '\131', '\040', '\145', '\162', '\040', '\061', + '\012', '\150', '\147', '\102', '\040', '\164', '\150', '\040', + '\061', '\012', '\160', '\167', '\102', '\040', '\160', '\162', + '\040', '\061', '\012', '\112', '\170', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\166', '\143', '\112', '\040', + '\143', '\150', '\040', '\061', '\012', '\163', '\131', '\167', + '\040', '\163', '\164', '\040', '\061', '\012', '\124', '\161', + '\170', '\040', '\161', '\165', '\040', '\061', '\012', '\145', + '\112', '\146', '\040', '\154', '\145', '\040', '\061', '\012', + '\143', '\172', '\112', '\040', '\143', '\150', '\040', '\061', + '\012', '\121', '\171', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\142', '\166', '\126', '\040', '\166', '\141', + '\040', '\061', '\012', '\130', '\171', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\146', '\152', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\144', '\131', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\160', '\102', + '\170', '\040', '\160', '\162', '\040', '\061', '\012', '\152', + '\166', '\122', '\040', '\151', '\152', '\040', '\061', '\012', + '\147', '\142', '\110', '\040', '\156', '\147', '\040', '\061', + '\012', '\171', '\147', '\110', '\040', '\156', '\147', '\040', + '\061', '\012', '\150', '\142', '\126', '\040', '\164', '\150', + '\040', '\061', '\012', '\154', '\167', '\125', '\040', '\154', + '\145', '\040', '\061', '\012', '\164', '\112', '\153', '\040', + '\164', '\150', '\040', '\061', '\012', '\160', '\111', '\167', + '\040', '\160', '\162', '\040', '\061', '\012', '\126', '\152', + '\154', '\040', '\154', '\145', '\040', '\061', '\012', '\104', + '\147', '\155', '\040', '\156', '\147', '\040', '\061', '\012', + '\156', '\166', '\122', '\040', '\141', '\156', '\040', '\061', + '\012', '\171', '\122', '\160', '\040', '\160', '\162', '\040', + '\061', '\012', '\146', '\117', '\152', '\040', '\151', '\152', + '\040', '\061', '\012', '\105', '\143', '\146', '\040', '\143', + '\150', '\040', '\061', '\012', '\132', '\162', '\146', '\040', + '\145', '\162', '\040', '\061', '\012', '\155', '\170', '\104', + '\040', '\155', '\145', '\040', '\061', '\012', '\111', '\161', + '\146', '\040', '\161', '\165', '\040', '\061', '\012', '\172', + '\102', '\152', '\040', '\163', '\172', '\040', '\061', '\012', + '\164', '\124', '\163', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\161', '\102', '\040', '\161', '\165', '\040', + '\061', '\012', '\153', '\103', '\166', '\040', '\153', '\141', + '\040', '\061', '\012', '\156', '\126', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\152', '\107', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\143', '\147', '\121', + '\040', '\143', '\150', '\040', '\061', '\012', '\120', '\160', + '\144', '\040', '\144', '\145', '\040', '\061', '\012', '\112', + '\143', '\144', '\040', '\143', '\150', '\040', '\061', '\012', + '\150', '\150', '\120', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\114', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\170', '\131', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\121', '\160', '\163', '\040', '\163', + '\164', '\040', '\061', '\012', '\163', '\146', '\105', '\040', + '\163', '\164', '\040', '\061', '\012', '\167', '\170', '\122', + '\040', '\167', '\141', '\040', '\061', '\012', '\160', '\106', + '\160', '\040', '\160', '\162', '\040', '\061', '\012', '\131', + '\155', '\146', '\040', '\155', '\145', '\040', '\061', '\012', + '\112', '\147', '\171', '\040', '\156', '\147', '\040', '\061', + '\012', '\171', '\166', '\111', '\040', '\166', '\141', '\040', + '\061', '\012', '\116', '\143', '\172', '\040', '\143', '\150', + '\040', '\061', '\012', '\167', '\102', '\146', '\040', '\167', + '\141', '\040', '\061', '\012', '\162', '\126', '\170', '\040', + '\145', '\162', '\040', '\061', '\012', '\152', '\166', '\130', + '\040', '\151', '\152', '\040', '\061', '\012', '\156', '\131', + '\160', '\040', '\141', '\156', '\040', '\061', '\012', '\156', + '\116', '\142', '\040', '\141', '\156', '\040', '\061', '\012', + '\143', '\121', '\151', '\040', '\143', '\150', '\040', '\061', + '\012', '\121', '\167', '\171', '\040', '\167', '\141', '\040', + '\061', '\012', '\166', '\120', '\146', '\040', '\166', '\141', + '\040', '\061', '\012', '\161', '\166', '\144', '\040', '\161', + '\165', '\040', '\061', '\012', '\150', '\153', '\104', '\040', + '\164', '\150', '\040', '\061', '\012', '\127', '\155', '\162', + '\040', '\145', '\162', '\040', '\061', '\012', '\147', '\144', + '\131', '\040', '\156', '\147', '\040', '\061', '\012', '\113', + '\152', '\152', '\040', '\151', '\152', '\040', '\061', '\012', + '\161', '\163', '\116', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\112', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\155', '\104', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\153', '\166', '\106', '\040', '\153', + '\141', '\040', '\061', '\012', '\153', '\127', '\170', '\040', + '\153', '\141', '\040', '\061', '\012', '\170', '\131', '\165', + '\040', '\161', '\165', '\040', '\061', '\012', '\145', '\115', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\155', + '\131', '\171', '\040', '\155', '\145', '\040', '\061', '\012', + '\110', '\170', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\142', '\115', '\040', '\160', '\162', '\040', + '\061', '\012', '\110', '\167', '\144', '\040', '\144', '\145', + '\040', '\061', '\012', '\155', '\127', '\165', '\040', '\161', + '\165', '\040', '\061', '\012', '\172', '\116', '\163', '\040', + '\163', '\164', '\040', '\061', '\012', '\121', '\152', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\141', '\161', + '\104', '\040', '\141', '\156', '\040', '\061', '\012', '\107', + '\143', '\144', '\040', '\143', '\150', '\040', '\061', '\012', + '\142', '\164', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\161', '\154', '\040', '\161', '\165', '\040', + '\061', '\012', '\125', '\152', '\167', '\040', '\151', '\152', + '\040', '\061', '\012', '\171', '\166', '\115', '\040', '\166', + '\141', '\040', '\061', '\012', '\110', '\150', '\167', '\040', + '\164', '\150', '\040', '\061', '\012', '\172', '\127', '\144', + '\040', '\163', '\172', '\040', '\061', '\012', '\160', '\131', + '\152', '\040', '\151', '\152', '\040', '\061', '\012', '\170', + '\127', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\171', '\154', '\117', '\040', '\154', '\145', '\040', '\061', + '\012', '\143', '\156', '\130', '\040', '\143', '\150', '\040', + '\061', '\012', '\143', '\115', '\146', '\040', '\143', '\150', + '\040', '\061', '\012', '\160', '\113', '\142', '\040', '\160', + '\162', '\040', '\061', '\012', '\167', '\157', '\126', '\040', + '\157', '\156', '\040', '\061', '\012', '\146', '\172', '\107', + '\040', '\163', '\172', '\040', '\061', '\012', '\114', '\161', + '\142', '\040', '\161', '\165', '\040', '\061', '\012', '\145', + '\117', '\152', '\040', '\145', '\162', '\040', '\061', '\012', + '\107', '\164', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\154', '\130', '\040', '\143', '\150', '\040', + '\061', '\012', '\153', '\144', '\103', '\040', '\144', '\145', + '\040', '\061', '\012', '\143', '\146', '\161', '\040', '\143', + '\150', '\040', '\061', '\012', '\150', '\113', '\153', '\040', + '\164', '\150', '\040', '\061', '\012', '\143', '\112', '\151', + '\040', '\143', '\150', '\040', '\061', '\012', '\165', '\123', + '\142', '\040', '\161', '\165', '\040', '\061', '\012', '\152', + '\147', '\124', '\040', '\156', '\147', '\040', '\061', '\012', + '\164', '\143', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\116', '\166', '\040', '\161', '\165', '\040', + '\061', '\012', '\146', '\160', '\102', '\040', '\160', '\162', + '\040', '\061', '\012', '\166', '\120', '\167', '\040', '\166', + '\141', '\040', '\061', '\012', '\152', '\155', '\101', '\040', + '\151', '\152', '\040', '\061', '\012', '\144', '\170', '\111', + '\040', '\144', '\145', '\040', '\061', '\012', '\152', '\107', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\102', + '\166', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\161', '\162', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\120', '\170', '\040', '\141', '\156', '\040', + '\061', '\012', '\121', '\155', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\143', '\161', '\103', '\040', '\143', + '\150', '\040', '\061', '\012', '\153', '\106', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\112', '\164', '\146', + '\040', '\164', '\150', '\040', '\061', '\012', '\103', '\161', + '\172', '\040', '\161', '\165', '\040', '\061', '\012', '\162', + '\103', '\144', '\040', '\145', '\162', '\040', '\061', '\012', + '\132', '\155', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\144', '\126', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\107', '\167', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\143', '\167', '\120', '\040', '\143', + '\150', '\040', '\061', '\012', '\167', '\126', '\165', '\040', + '\161', '\165', '\040', '\061', '\012', '\144', '\116', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\152', '\130', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\115', + '\142', '\172', '\040', '\163', '\172', '\040', '\061', '\012', + '\167', '\166', '\107', '\040', '\166', '\145', '\040', '\061', + '\012', '\126', '\160', '\167', '\040', '\160', '\162', '\040', + '\061', '\012', '\171', '\130', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\150', '\154', '\113', '\040', '\164', + '\150', '\040', '\061', '\012', '\160', '\131', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\106', '\142', '\144', + '\040', '\144', '\145', '\040', '\061', '\012', '\172', '\143', + '\126', '\040', '\143', '\150', '\040', '\061', '\012', '\162', + '\121', '\153', '\040', '\145', '\162', '\040', '\061', '\012', + '\167', '\164', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\145', '\111', '\040', '\161', '\165', '\040', + '\061', '\012', '\145', '\107', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\153', '\115', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\153', '\161', '\123', '\040', + '\161', '\165', '\040', '\061', '\012', '\143', '\161', '\144', + '\040', '\143', '\150', '\040', '\061', '\012', '\160', '\114', + '\146', '\040', '\160', '\157', '\040', '\061', '\012', '\170', + '\166', '\117', '\040', '\166', '\141', '\040', '\061', '\012', + '\162', '\146', '\110', '\040', '\145', '\162', '\040', '\061', + '\012', '\147', '\111', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\120', '\161', '\153', '\040', '\161', '\165', + '\040', '\061', '\012', '\170', '\103', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\144', '\126', '\163', '\040', + '\163', '\164', '\040', '\061', '\012', '\151', '\161', '\131', + '\040', '\161', '\165', '\040', '\061', '\012', '\142', '\163', + '\112', '\040', '\163', '\164', '\040', '\061', '\012', '\126', + '\167', '\167', '\040', '\167', '\141', '\040', '\061', '\012', + '\132', '\156', '\155', '\040', '\141', '\156', '\040', '\061', + '\012', '\131', '\162', '\172', '\040', '\145', '\162', '\040', + '\061', '\012', '\122', '\166', '\172', '\040', '\163', '\172', + '\040', '\061', '\012', '\144', '\172', '\113', '\040', '\144', + '\145', '\040', '\061', '\012', '\172', '\142', '\127', '\040', + '\163', '\172', '\040', '\061', '\012', '\164', '\153', '\170', + '\040', '\164', '\150', '\040', '\061', '\012', '\170', '\153', + '\120', '\040', '\153', '\141', '\040', '\061', '\012', '\153', + '\172', '\123', '\040', '\163', '\172', '\040', '\061', '\012', + '\147', '\130', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\114', '\170', '\146', '\040', '\146', '\157', '\040', + '\061', '\012', '\106', '\167', '\162', '\040', '\145', '\162', + '\040', '\061', '\012', '\154', '\110', '\163', '\040', '\154', + '\145', '\040', '\061', '\012', '\172', '\162', '\102', '\040', + '\145', '\162', '\040', '\061', '\012', '\152', '\116', '\142', + '\040', '\151', '\152', '\040', '\061', '\012', '\110', '\170', + '\171', '\040', '\156', '\171', '\040', '\061', '\012', '\107', + '\146', '\167', '\040', '\167', '\141', '\040', '\061', '\012', + '\105', '\147', '\167', '\040', '\156', '\147', '\040', '\061', + '\012', '\112', '\170', '\167', '\040', '\167', '\141', '\040', + '\061', '\012', '\164', '\126', '\155', '\040', '\164', '\150', + '\040', '\061', '\012', '\142', '\167', '\121', '\040', '\167', + '\141', '\040', '\061', '\012', '\147', '\111', '\170', '\040', + '\156', '\147', '\040', '\061', '\012', '\127', '\161', '\165', + '\040', '\165', '\156', '\040', '\061', '\012', '\152', '\166', + '\111', '\040', '\151', '\152', '\040', '\061', '\012', '\143', + '\107', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\153', '\123', '\142', '\040', '\153', '\141', '\040', '\061', + '\012', '\150', '\170', '\107', '\040', '\164', '\150', '\040', + '\061', '\012', '\172', '\110', '\155', '\040', '\163', '\172', + '\040', '\061', '\012', '\112', '\160', '\153', '\040', '\153', + '\141', '\040', '\061', '\012', '\146', '\126', '\142', '\040', + '\142', '\145', '\040', '\061', '\012', '\125', '\153', '\146', + '\040', '\153', '\141', '\040', '\061', '\012', '\162', '\170', + '\106', '\040', '\145', '\162', '\040', '\061', '\012', '\144', + '\126', '\165', '\040', '\161', '\165', '\040', '\061', '\012', + '\163', '\144', '\130', '\040', '\163', '\164', '\040', '\061', + '\012', '\155', '\152', '\115', '\040', '\151', '\152', '\040', + '\061', '\012', '\170', '\167', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\117', '\147', '\153', '\040', '\156', + '\147', '\040', '\061', '\012', '\161', '\150', '\162', '\040', + '\164', '\150', '\040', '\061', '\012', '\166', '\146', '\101', + '\040', '\166', '\141', '\040', '\061', '\012', '\161', '\142', + '\101', '\040', '\161', '\165', '\040', '\061', '\012', '\114', + '\146', '\165', '\040', '\161', '\165', '\040', '\061', '\012', + '\150', '\172', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\110', '\146', '\040', '\151', '\156', '\040', + '\061', '\012', '\152', '\170', '\142', '\040', '\151', '\152', + '\040', '\061', '\012', '\166', '\155', '\120', '\040', '\166', + '\141', '\040', '\061', '\012', '\142', '\166', '\111', '\040', + '\166', '\141', '\040', '\061', '\012', '\146', '\155', '\110', + '\040', '\155', '\145', '\040', '\061', '\012', '\161', '\164', + '\170', '\040', '\164', '\150', '\040', '\061', '\012', '\142', + '\166', '\121', '\040', '\166', '\141', '\040', '\061', '\012', + '\161', '\172', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\126', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\130', '\155', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\161', '\130', '\157', '\040', '\161', + '\165', '\040', '\061', '\012', '\160', '\146', '\104', '\040', + '\160', '\162', '\040', '\061', '\012', '\146', '\103', '\144', + '\040', '\144', '\145', '\040', '\061', '\012', '\166', '\142', + '\170', '\040', '\166', '\141', '\040', '\061', '\012', '\132', + '\150', '\172', '\040', '\164', '\150', '\040', '\061', '\012', + '\113', '\167', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\162', '\143', '\112', '\040', '\143', '\150', '\040', + '\061', '\012', '\152', '\154', '\124', '\040', '\154', '\145', + '\040', '\061', '\012', '\152', '\172', '\115', '\040', '\163', + '\172', '\040', '\061', '\012', '\162', '\160', '\120', '\040', + '\145', '\162', '\040', '\061', '\012', '\164', '\155', '\101', + '\040', '\164', '\150', '\040', '\061', '\012', '\141', '\131', + '\167', '\040', '\141', '\156', '\040', '\061', '\012', '\172', + '\102', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\170', '\150', '\124', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\114', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\143', '\113', '\146', '\040', '\143', '\150', + '\040', '\061', '\012', '\161', '\144', '\120', '\040', '\161', + '\165', '\040', '\061', '\012', '\131', '\142', '\170', '\040', + '\142', '\145', '\040', '\061', '\012', '\144', '\110', '\163', + '\040', '\163', '\164', '\040', '\061', '\012', '\152', '\150', + '\110', '\040', '\164', '\150', '\040', '\061', '\012', '\102', + '\163', '\166', '\040', '\163', '\164', '\040', '\061', '\012', + '\162', '\132', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\150', '\112', '\040', '\164', '\150', '\040', + '\061', '\012', '\132', '\167', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\153', '\130', '\146', '\040', '\153', + '\141', '\040', '\061', '\012', '\172', '\166', '\124', '\040', + '\163', '\172', '\040', '\061', '\012', '\171', '\151', '\103', + '\040', '\151', '\156', '\040', '\061', '\012', '\147', '\153', + '\124', '\040', '\156', '\147', '\040', '\061', '\012', '\156', + '\112', '\167', '\040', '\141', '\156', '\040', '\061', '\012', + '\172', '\160', '\126', '\040', '\163', '\172', '\040', '\061', + '\012', '\164', '\120', '\161', '\040', '\164', '\150', '\040', + '\061', '\012', '\143', '\126', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\144', '\102', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\143', '\122', '\146', '\040', + '\143', '\150', '\040', '\061', '\012', '\166', '\122', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\152', '\147', + '\101', '\040', '\156', '\147', '\040', '\061', '\012', '\142', + '\115', '\172', '\040', '\163', '\172', '\040', '\061', '\012', + '\150', '\112', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\110', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\103', '\153', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\161', '\143', '\152', '\040', '\143', + '\150', '\040', '\061', '\012', '\171', '\111', '\142', '\040', + '\142', '\145', '\040', '\061', '\012', '\167', '\161', '\105', + '\040', '\161', '\165', '\040', '\061', '\012', '\160', '\115', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\110', + '\161', '\152', '\040', '\161', '\165', '\040', '\061', '\012', + '\152', '\132', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\151', '\161', '\117', '\040', '\161', '\165', '\040', + '\061', '\012', '\164', '\161', '\103', '\040', '\164', '\150', + '\040', '\061', '\012', '\161', '\157', '\113', '\040', '\161', + '\165', '\040', '\061', '\012', '\113', '\156', '\161', '\040', + '\141', '\156', '\040', '\061', '\012', '\142', '\121', '\155', + '\040', '\155', '\145', '\040', '\061', '\012', '\165', '\165', + '\130', '\040', '\161', '\165', '\040', '\061', '\012', '\127', + '\172', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\120', '\170', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\121', '\147', '\146', '\040', '\156', '\147', '\040', + '\061', '\012', '\163', '\106', '\167', '\040', '\163', '\164', + '\040', '\061', '\012', '\147', '\110', '\146', '\040', '\156', + '\147', '\040', '\061', '\012', '\153', '\147', '\116', '\040', + '\156', '\147', '\040', '\061', '\012', '\162', '\103', '\167', + '\040', '\145', '\162', '\040', '\061', '\012', '\131', '\152', + '\171', '\040', '\151', '\152', '\040', '\061', '\012', '\160', + '\156', '\126', '\040', '\141', '\156', '\040', '\061', '\012', + '\146', '\142', '\123', '\040', '\142', '\145', '\040', '\061', + '\012', '\151', '\110', '\172', '\040', '\151', '\156', '\040', + '\061', '\012', '\153', '\107', '\170', '\040', '\153', '\141', + '\040', '\061', '\012', '\153', '\167', '\123', '\040', '\153', + '\141', '\040', '\061', '\012', '\163', '\104', '\155', '\040', + '\163', '\164', '\040', '\061', '\012', '\126', '\150', '\153', + '\040', '\164', '\150', '\040', '\061', '\012', '\160', '\150', + '\116', '\040', '\164', '\150', '\040', '\061', '\012', '\112', + '\142', '\146', '\040', '\142', '\145', '\040', '\061', '\012', + '\160', '\127', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\166', '\121', '\040', '\166', '\141', '\040', + '\061', '\012', '\166', '\116', '\155', '\040', '\166', '\141', + '\040', '\061', '\012', '\154', '\131', '\167', '\040', '\154', + '\145', '\040', '\061', '\012', '\172', '\110', '\170', '\040', + '\163', '\172', '\040', '\061', '\012', '\132', '\172', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\142', '\104', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\106', + '\143', '\166', '\040', '\143', '\150', '\040', '\061', '\012', + '\144', '\112', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\121', '\167', '\142', '\040', '\167', '\141', '\040', + '\061', '\012', '\161', '\106', '\167', '\040', '\161', '\165', + '\040', '\061', '\012', '\167', '\155', '\117', '\040', '\155', + '\145', '\040', '\061', '\012', '\102', '\166', '\171', '\040', + '\166', '\141', '\040', '\061', '\012', '\161', '\147', '\131', + '\040', '\161', '\165', '\040', '\061', '\012', '\166', '\131', + '\163', '\040', '\163', '\164', '\040', '\061', '\012', '\170', + '\167', '\106', '\040', '\167', '\141', '\040', '\061', '\012', + '\161', '\167', '\120', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\105', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\155', '\127', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\146', '\172', '\117', '\040', '\163', + '\172', '\040', '\061', '\012', '\142', '\120', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\160', '\156', '\127', + '\040', '\141', '\156', '\040', '\061', '\012', '\150', '\107', + '\170', '\040', '\164', '\150', '\040', '\061', '\012', '\126', + '\153', '\153', '\040', '\153', '\141', '\040', '\061', '\012', + '\130', '\162', '\170', '\040', '\145', '\162', '\040', '\061', + '\012', '\147', '\112', '\144', '\040', '\156', '\147', '\040', + '\061', '\012', '\114', '\154', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\126', '\161', '\165', '\040', '\165', + '\156', '\040', '\061', '\012', '\146', '\147', '\110', '\040', + '\156', '\147', '\040', '\061', '\012', '\126', '\143', '\171', + '\040', '\143', '\150', '\040', '\061', '\012', '\150', '\126', + '\143', '\040', '\164', '\150', '\040', '\061', '\012', '\162', + '\167', '\132', '\040', '\145', '\162', '\040', '\061', '\012', + '\130', '\154', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\170', '\112', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\106', '\156', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\131', '\160', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\154', '\150', '\112', '\040', + '\164', '\150', '\040', '\061', '\012', '\141', '\125', '\152', + '\040', '\141', '\156', '\040', '\061', '\012', '\154', '\102', + '\160', '\040', '\160', '\162', '\040', '\061', '\012', '\144', + '\154', '\127', '\040', '\154', '\145', '\040', '\061', '\012', + '\160', '\166', '\126', '\040', '\166', '\141', '\040', '\061', + '\012', '\115', '\167', '\162', '\040', '\145', '\162', '\040', + '\061', '\012', '\132', '\167', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\167', '\143', '\125', '\040', '\143', + '\150', '\040', '\061', '\012', '\143', '\126', '\161', '\040', + '\143', '\150', '\040', '\061', '\012', '\171', '\143', '\125', + '\040', '\143', '\150', '\040', '\061', '\012', '\114', '\143', + '\161', '\040', '\143', '\150', '\040', '\061', '\012', '\162', + '\166', '\121', '\040', '\145', '\162', '\040', '\061', '\012', + '\145', '\131', '\155', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\103', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\144', '\102', '\170', '\040', '\144', '\145', + '\040', '\061', '\012', '\111', '\167', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\147', '\115', '\164', '\040', + '\164', '\150', '\040', '\061', '\012', '\142', '\150', '\103', + '\040', '\164', '\150', '\040', '\061', '\012', '\142', '\104', + '\163', '\040', '\163', '\164', '\040', '\061', '\012', '\126', + '\150', '\172', '\040', '\164', '\150', '\040', '\061', '\012', + '\153', '\112', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\117', '\150', '\172', '\040', '\164', '\150', '\040', + '\061', '\012', '\153', '\104', '\172', '\040', '\163', '\172', + '\040', '\061', '\012', '\150', '\124', '\156', '\040', '\164', + '\150', '\040', '\061', '\012', '\145', '\161', '\107', '\040', + '\161', '\165', '\040', '\061', '\012', '\147', '\112', '\162', + '\040', '\156', '\147', '\040', '\061', '\012', '\132', '\160', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\150', + '\167', '\121', '\040', '\164', '\150', '\040', '\061', '\012', + '\146', '\147', '\131', '\040', '\156', '\147', '\040', '\061', + '\012', '\163', '\144', '\126', '\040', '\163', '\164', '\040', + '\061', '\012', '\154', '\152', '\126', '\040', '\154', '\145', + '\040', '\061', '\012', '\171', '\107', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\165', '\127', '\147', '\040', + '\161', '\165', '\040', '\061', '\012', '\163', '\142', '\117', + '\040', '\163', '\164', '\040', '\061', '\012', '\161', '\144', + '\104', '\040', '\161', '\165', '\040', '\061', '\012', '\171', + '\112', '\152', '\040', '\151', '\152', '\040', '\061', '\012', + '\156', '\167', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\101', '\160', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\143', '\143', '\113', '\040', '\143', '\150', + '\040', '\061', '\012', '\121', '\167', '\154', '\040', '\154', + '\145', '\040', '\061', '\012', '\157', '\171', '\121', '\040', + '\157', '\156', '\040', '\061', '\012', '\154', '\120', '\167', + '\040', '\154', '\145', '\040', '\061', '\012', '\143', '\131', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\142', + '\162', '\107', '\040', '\145', '\162', '\040', '\061', '\012', + '\170', '\153', '\124', '\040', '\153', '\141', '\040', '\061', + '\012', '\144', '\125', '\152', '\040', '\144', '\145', '\040', + '\061', '\012', '\162', '\150', '\122', '\040', '\164', '\150', + '\040', '\061', '\012', '\170', '\120', '\167', '\040', '\167', + '\141', '\040', '\061', '\012', '\170', '\157', '\106', '\040', + '\157', '\156', '\040', '\061', '\012', '\150', '\131', '\152', + '\040', '\164', '\150', '\040', '\061', '\012', '\150', '\131', + '\167', '\040', '\164', '\150', '\040', '\061', '\012', '\154', + '\120', '\156', '\040', '\141', '\156', '\040', '\061', '\012', + '\172', '\103', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\163', '\112', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\167', '\104', '\163', '\040', '\163', '\164', + '\040', '\061', '\012', '\146', '\126', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\172', '\167', '\127', '\040', + '\163', '\172', '\040', '\061', '\012', '\171', '\114', '\152', + '\040', '\151', '\152', '\040', '\061', '\012', '\141', '\102', + '\170', '\040', '\141', '\156', '\040', '\061', '\012', '\104', + '\166', '\166', '\040', '\166', '\141', '\040', '\061', '\012', + '\164', '\113', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\146', '\107', '\040', '\151', '\152', '\040', + '\061', '\012', '\170', '\115', '\155', '\040', '\155', '\145', + '\040', '\061', '\012', '\142', '\114', '\160', '\040', '\160', + '\162', '\040', '\061', '\012', '\170', '\167', '\127', '\040', + '\167', '\141', '\040', '\061', '\012', '\142', '\172', '\110', + '\040', '\163', '\172', '\040', '\061', '\012', '\143', '\111', + '\167', '\040', '\143', '\150', '\040', '\061', '\012', '\172', + '\144', '\116', '\040', '\163', '\172', '\040', '\061', '\012', + '\107', '\147', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\154', '\167', '\126', '\040', '\154', '\145', '\040', + '\061', '\012', '\161', '\171', '\126', '\040', '\161', '\165', + '\040', '\061', '\012', '\166', '\102', '\166', '\040', '\166', + '\141', '\040', '\061', '\012', '\117', '\167', '\155', '\040', + '\155', '\145', '\040', '\061', '\012', '\114', '\164', '\170', + '\040', '\164', '\150', '\040', '\061', '\012', '\155', '\161', + '\105', '\040', '\161', '\165', '\040', '\061', '\012', '\130', + '\152', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\160', '\172', '\131', '\040', '\163', '\172', '\040', '\061', + '\012', '\112', '\144', '\163', '\040', '\163', '\164', '\040', + '\061', '\012', '\153', '\115', '\154', '\040', '\154', '\145', + '\040', '\061', '\012', '\104', '\144', '\152', '\040', '\144', + '\145', '\040', '\061', '\012', '\164', '\146', '\130', '\040', + '\164', '\150', '\040', '\061', '\012', '\143', '\161', '\124', + '\040', '\143', '\150', '\040', '\061', '\012', '\142', '\165', + '\107', '\040', '\161', '\165', '\040', '\061', '\012', '\157', + '\110', '\142', '\040', '\160', '\157', '\040', '\061', '\012', + '\166', '\122', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\171', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\153', '\160', '\131', '\040', '\153', '\141', + '\040', '\061', '\012', '\166', '\161', '\116', '\040', '\161', + '\165', '\040', '\061', '\012', '\152', '\116', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\143', '\127', '\142', + '\040', '\143', '\150', '\040', '\061', '\012', '\147', '\142', + '\112', '\040', '\156', '\147', '\040', '\061', '\012', '\157', + '\132', '\167', '\040', '\157', '\156', '\040', '\061', '\012', + '\143', '\102', '\172', '\040', '\143', '\150', '\040', '\061', + '\012', '\120', '\166', '\166', '\040', '\166', '\141', '\040', + '\061', '\012', '\154', '\152', '\111', '\040', '\154', '\145', + '\040', '\061', '\012', '\150', '\166', '\121', '\040', '\164', + '\150', '\040', '\061', '\012', '\153', '\167', '\131', '\040', + '\153', '\141', '\040', '\061', '\012', '\150', '\102', '\147', + '\040', '\164', '\150', '\040', '\061', '\012', '\153', '\144', + '\116', '\040', '\144', '\145', '\040', '\061', '\012', '\171', + '\170', '\110', '\040', '\156', '\171', '\040', '\061', '\012', + '\146', '\170', '\110', '\040', '\146', '\157', '\040', '\061', + '\012', '\164', '\130', '\152', '\040', '\164', '\150', '\040', + '\061', '\012', '\165', '\102', '\170', '\040', '\161', '\165', + '\040', '\061', '\012', '\165', '\112', '\155', '\040', '\161', + '\165', '\040', '\061', '\012', '\107', '\170', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\146', '\152', '\113', + '\040', '\151', '\152', '\040', '\061', '\012', '\147', '\161', + '\117', '\040', '\161', '\165', '\040', '\061', '\012', '\144', + '\115', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\154', '\126', '\170', '\040', '\154', '\145', '\040', '\061', + '\012', '\122', '\150', '\160', '\040', '\164', '\150', '\040', + '\061', '\012', '\143', '\104', '\156', '\040', '\143', '\150', + '\040', '\061', '\012', '\130', '\153', '\166', '\040', '\153', + '\141', '\040', '\061', '\012', '\172', '\155', '\102', '\040', + '\163', '\172', '\040', '\061', '\012', '\161', '\141', '\131', + '\040', '\141', '\156', '\040', '\061', '\012', '\111', '\166', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\167', + '\155', '\120', '\040', '\155', '\145', '\040', '\061', '\012', + '\142', '\152', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\155', '\125', '\040', '\143', '\150', '\040', + '\061', '\012', '\163', '\154', '\103', '\040', '\154', '\145', + '\040', '\061', '\012', '\113', '\162', '\170', '\040', '\145', + '\162', '\040', '\061', '\012', '\151', '\126', '\166', '\040', + '\151', '\156', '\040', '\061', '\012', '\132', '\167', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\171', '\120', + '\144', '\040', '\144', '\145', '\040', '\061', '\012', '\161', + '\125', '\166', '\040', '\161', '\165', '\040', '\061', '\012', + '\120', '\144', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\121', '\172', '\153', '\040', '\163', '\172', '\040', + '\061', '\012', '\172', '\157', '\125', '\040', '\157', '\156', + '\040', '\061', '\012', '\170', '\112', '\146', '\040', '\146', + '\157', '\040', '\061', '\012', '\125', '\144', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\121', '\167', '\152', + '\040', '\151', '\152', '\040', '\061', '\012', '\113', '\166', + '\144', '\040', '\144', '\145', '\040', '\061', '\012', '\166', + '\121', '\167', '\040', '\166', '\141', '\040', '\061', '\012', + '\122', '\144', '\153', '\040', '\144', '\145', '\040', '\061', + '\012', '\163', '\111', '\152', '\040', '\163', '\164', '\040', + '\061', '\012', '\107', '\147', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\154', '\116', '\167', '\040', '\154', + '\145', '\040', '\061', '\012', '\161', '\166', '\162', '\040', + '\161', '\165', '\040', '\061', '\012', '\171', '\161', '\104', + '\040', '\161', '\165', '\040', '\061', '\012', '\146', '\130', + '\154', '\040', '\154', '\145', '\040', '\061', '\012', '\152', + '\161', '\147', '\040', '\161', '\165', '\040', '\061', '\012', + '\161', '\155', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\124', '\147', '\144', '\040', '\156', '\147', '\040', + '\061', '\012', '\172', '\160', '\117', '\040', '\160', '\157', + '\040', '\061', '\012', '\164', '\105', '\172', '\040', '\164', + '\150', '\040', '\061', '\012', '\102', '\161', '\172', '\040', + '\161', '\165', '\040', '\061', '\012', '\167', '\146', '\114', + '\040', '\167', '\141', '\040', '\061', '\012', '\166', '\131', + '\165', '\040', '\161', '\165', '\040', '\061', '\012', '\104', + '\170', '\167', '\040', '\167', '\141', '\040', '\061', '\012', + '\161', '\127', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\122', '\172', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\155', '\121', '\157', '\040', '\157', '\156', + '\040', '\061', '\012', '\124', '\164', '\143', '\040', '\164', + '\150', '\040', '\061', '\012', '\164', '\126', '\166', '\040', + '\164', '\150', '\040', '\061', '\012', '\122', '\161', '\156', + '\040', '\141', '\156', '\040', '\061', '\012', '\127', '\143', + '\156', '\040', '\143', '\150', '\040', '\061', '\012', '\116', + '\167', '\165', '\040', '\161', '\165', '\040', '\061', '\012', + '\170', '\157', '\112', '\040', '\157', '\156', '\040', '\061', + '\012', '\166', '\104', '\146', '\040', '\166', '\141', '\040', + '\061', '\012', '\160', '\150', '\110', '\040', '\164', '\150', + '\040', '\061', '\012', '\146', '\112', '\163', '\040', '\163', + '\164', '\040', '\061', '\012', '\120', '\170', '\155', '\040', + '\155', '\145', '\040', '\061', '\012', '\162', '\106', '\142', + '\040', '\145', '\162', '\040', '\061', '\012', '\150', '\154', + '\115', '\040', '\164', '\150', '\040', '\061', '\012', '\155', + '\153', '\130', '\040', '\153', '\141', '\040', '\061', '\012', + '\156', '\156', '\121', '\040', '\141', '\156', '\040', '\061', + '\012', '\130', '\146', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\163', '\142', '\132', '\040', '\163', '\164', + '\040', '\061', '\012', '\131', '\171', '\146', '\040', '\156', + '\171', '\040', '\061', '\012', '\102', '\152', '\167', '\040', + '\151', '\152', '\040', '\061', '\012', '\111', '\154', '\170', + '\040', '\154', '\145', '\040', '\061', '\012', '\161', '\160', + '\101', '\040', '\161', '\165', '\040', '\061', '\012', '\115', + '\161', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\147', '\161', '\132', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\116', '\166', '\040', '\163', '\164', '\040', + '\061', '\012', '\132', '\166', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\153', '\123', '\170', '\040', '\153', + '\141', '\040', '\061', '\012', '\166', '\102', '\144', '\040', + '\144', '\145', '\040', '\061', '\012', '\167', '\166', '\132', + '\040', '\166', '\141', '\040', '\061', '\012', '\125', '\157', + '\145', '\040', '\145', '\162', '\040', '\061', '\012', '\106', + '\152', '\171', '\040', '\151', '\152', '\040', '\061', '\012', + '\172', '\113', '\142', '\040', '\163', '\172', '\040', '\061', + '\012', '\160', '\166', '\111', '\040', '\166', '\141', '\040', + '\061', '\012', '\132', '\154', '\154', '\040', '\154', '\145', + '\040', '\061', '\012', '\150', '\144', '\105', '\040', '\164', + '\150', '\040', '\061', '\012', '\106', '\160', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\154', '\150', '\126', + '\040', '\164', '\150', '\040', '\061', '\012', '\162', '\161', + '\121', '\040', '\161', '\165', '\040', '\061', '\012', '\167', + '\152', '\107', '\040', '\151', '\152', '\040', '\061', '\012', + '\160', '\114', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\142', '\160', '\112', '\040', '\160', '\162', '\040', + '\061', '\012', '\167', '\172', '\126', '\040', '\163', '\172', + '\040', '\061', '\012', '\110', '\147', '\161', '\040', '\156', + '\147', '\040', '\061', '\012', '\172', '\150', '\127', '\040', + '\164', '\150', '\040', '\061', '\012', '\114', '\166', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\130', '\150', + '\162', '\040', '\164', '\150', '\040', '\061', '\012', '\161', + '\165', '\131', '\040', '\165', '\156', '\040', '\061', '\012', + '\152', '\161', '\132', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\165', '\110', '\040', '\161', '\165', '\040', + '\061', '\012', '\106', '\172', '\152', '\040', '\163', '\172', + '\040', '\061', '\012', '\147', '\172', '\107', '\040', '\156', + '\147', '\040', '\061', '\012', '\164', '\106', '\143', '\040', + '\164', '\150', '\040', '\061', '\012', '\166', '\146', '\105', + '\040', '\166', '\141', '\040', '\061', '\012', '\111', '\147', + '\170', '\040', '\156', '\147', '\040', '\061', '\012', '\146', + '\161', '\131', '\040', '\161', '\165', '\040', '\061', '\012', + '\147', '\131', '\142', '\040', '\156', '\147', '\040', '\061', + '\012', '\154', '\112', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\167', '\143', '\117', '\040', '\143', '\150', + '\040', '\061', '\012', '\121', '\166', '\153', '\040', '\153', + '\141', '\040', '\061', '\012', '\124', '\161', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\142', '\144', '\131', + '\040', '\144', '\145', '\040', '\061', '\012', '\167', '\165', + '\124', '\040', '\161', '\165', '\040', '\061', '\012', '\154', + '\110', '\167', '\040', '\154', '\145', '\040', '\061', '\012', + '\172', '\122', '\155', '\040', '\163', '\172', '\040', '\061', + '\012', '\110', '\147', '\167', '\040', '\156', '\147', '\040', + '\061', '\012', '\164', '\120', '\153', '\040', '\164', '\150', + '\040', '\061', '\012', '\112', '\161', '\166', '\040', '\161', + '\165', '\040', '\061', '\012', '\164', '\113', '\170', '\040', + '\164', '\150', '\040', '\061', '\012', '\170', '\160', '\101', + '\040', '\160', '\162', '\040', '\061', '\012', '\142', '\153', + '\111', '\040', '\153', '\141', '\040', '\061', '\012', '\142', + '\123', '\152', '\040', '\151', '\152', '\040', '\061', '\012', + '\155', '\170', '\127', '\040', '\155', '\145', '\040', '\061', + '\012', '\155', '\152', '\122', '\040', '\151', '\152', '\040', + '\061', '\012', '\117', '\151', '\160', '\040', '\151', '\156', + '\040', '\061', '\012', '\167', '\171', '\131', '\040', '\167', + '\141', '\040', '\061', '\012', '\144', '\106', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\161', '\104', '\147', + '\040', '\161', '\165', '\040', '\061', '\012', '\167', '\130', + '\160', '\040', '\160', '\162', '\040', '\061', '\012', '\126', + '\142', '\160', '\040', '\160', '\162', '\040', '\061', '\012', + '\152', '\171', '\116', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\166', '\120', '\040', '\166', '\141', '\040', + '\061', '\012', '\171', '\126', '\162', '\040', '\145', '\162', + '\040', '\061', '\012', '\141', '\127', '\155', '\040', '\141', + '\156', '\040', '\061', '\012', '\107', '\152', '\153', '\040', + '\151', '\152', '\040', '\061', '\012', '\101', '\160', '\167', + '\040', '\160', '\162', '\040', '\061', '\012', '\132', '\163', + '\167', '\040', '\163', '\164', '\040', '\061', '\012', '\152', + '\121', '\166', '\040', '\151', '\152', '\040', '\061', '\012', + '\152', '\142', '\124', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\144', '\102', '\040', '\144', '\145', '\040', + '\061', '\012', '\153', '\143', '\131', '\040', '\143', '\150', + '\040', '\061', '\012', '\162', '\161', '\103', '\040', '\161', + '\165', '\040', '\061', '\012', '\142', '\170', '\104', '\040', + '\142', '\145', '\040', '\061', '\012', '\166', '\154', '\170', + '\040', '\154', '\145', '\040', '\061', '\012', '\153', '\152', + '\112', '\040', '\151', '\152', '\040', '\061', '\012', '\170', + '\161', '\127', '\040', '\161', '\165', '\040', '\061', '\012', + '\172', '\170', '\105', '\040', '\163', '\172', '\040', '\061', + '\012', '\163', '\110', '\146', '\040', '\163', '\164', '\040', + '\061', '\012', '\152', '\165', '\106', '\040', '\161', '\165', + '\040', '\061', '\012', '\153', '\167', '\130', '\040', '\153', + '\141', '\040', '\061', '\012', '\157', '\161', '\127', '\040', + '\161', '\165', '\040', '\061', '\012', '\161', '\127', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\146', '\110', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\143', + '\110', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\112', '\152', '\155', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\142', '\101', '\040', '\142', '\145', '\040', + '\061', '\012', '\122', '\161', '\152', '\040', '\161', '\165', + '\040', '\061', '\012', '\111', '\152', '\171', '\040', '\151', + '\152', '\040', '\061', '\012', '\166', '\123', '\170', '\040', + '\166', '\141', '\040', '\061', '\012', '\160', '\126', '\152', + '\040', '\151', '\152', '\040', '\061', '\012', '\162', '\121', + '\170', '\040', '\145', '\162', '\040', '\061', '\012', '\146', + '\155', '\113', '\040', '\155', '\145', '\040', '\061', '\012', + '\146', '\156', '\101', '\040', '\141', '\156', '\040', '\061', + '\012', '\120', '\150', '\166', '\040', '\164', '\150', '\040', + '\061', '\012', '\142', '\150', '\116', '\040', '\164', '\150', + '\040', '\061', '\012', '\110', '\170', '\160', '\040', '\160', + '\162', '\040', '\061', '\012', '\126', '\152', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\154', '\161', '\103', + '\040', '\161', '\165', '\040', '\061', '\012', '\127', '\150', + '\144', '\040', '\164', '\150', '\040', '\061', '\012', '\172', + '\163', '\106', '\040', '\163', '\164', '\040', '\061', '\012', + '\164', '\131', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\112', '\172', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\116', '\146', '\146', '\040', '\146', '\157', + '\040', '\061', '\012', '\161', '\130', '\163', '\040', '\161', + '\165', '\040', '\061', '\012', '\170', '\112', '\152', '\040', + '\151', '\152', '\040', '\061', '\012', '\154', '\130', '\156', + '\040', '\141', '\156', '\040', '\061', '\012', '\132', '\160', + '\166', '\040', '\166', '\141', '\040', '\061', '\012', '\161', + '\124', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\156', '\160', '\110', '\040', '\141', '\156', '\040', '\061', + '\012', '\153', '\131', '\170', '\040', '\153', '\141', '\040', + '\061', '\012', '\142', '\102', '\163', '\040', '\163', '\164', + '\040', '\061', '\012', '\166', '\105', '\141', '\040', '\141', + '\156', '\040', '\061', '\012', '\160', '\152', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\161', '\111', '\151', + '\040', '\161', '\165', '\040', '\061', '\012', '\106', '\144', + '\153', '\040', '\144', '\145', '\040', '\061', '\012', '\146', + '\116', '\170', '\040', '\146', '\157', '\040', '\061', '\012', + '\117', '\146', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\130', '\145', '\040', '\145', '\162', '\040', + '\061', '\012', '\155', '\166', '\132', '\040', '\166', '\141', + '\040', '\061', '\012', '\103', '\152', '\163', '\040', '\163', + '\164', '\040', '\061', '\012', '\106', '\155', '\155', '\040', + '\155', '\145', '\040', '\061', '\012', '\160', '\153', '\122', + '\040', '\153', '\141', '\040', '\061', '\012', '\172', '\146', + '\132', '\040', '\163', '\172', '\040', '\061', '\012', '\132', + '\160', '\155', '\040', '\155', '\145', '\040', '\061', '\012', + '\143', '\142', '\101', '\040', '\143', '\150', '\040', '\061', + '\012', '\164', '\166', '\131', '\040', '\164', '\150', '\040', + '\061', '\012', '\114', '\155', '\160', '\040', '\155', '\145', + '\040', '\061', '\012', '\147', '\106', '\144', '\040', '\156', + '\147', '\040', '\061', '\012', '\142', '\106', '\170', '\040', + '\142', '\145', '\040', '\061', '\012', '\106', '\152', '\155', + '\040', '\151', '\152', '\040', '\061', '\012', '\167', '\152', + '\106', '\040', '\151', '\152', '\040', '\061', '\012', '\142', + '\152', '\166', '\040', '\151', '\152', '\040', '\061', '\012', + '\144', '\142', '\124', '\040', '\144', '\145', '\040', '\061', + '\012', '\152', '\155', '\121', '\040', '\151', '\152', '\040', + '\061', '\012', '\170', '\106', '\167', '\040', '\167', '\141', + '\040', '\061', '\012', '\143', '\104', '\153', '\040', '\143', + '\150', '\040', '\061', '\012', '\150', '\106', '\172', '\040', + '\164', '\150', '\040', '\061', '\012', '\165', '\107', '\155', + '\040', '\161', '\165', '\040', '\061', '\012', '\131', '\150', + '\170', '\040', '\164', '\150', '\040', '\061', '\012', '\126', + '\164', '\154', '\040', '\164', '\150', '\040', '\061', '\012', + '\141', '\172', '\126', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\112', '\163', '\040', '\163', '\164', '\040', + '\061', '\012', '\115', '\170', '\167', '\040', '\167', '\141', + '\040', '\061', '\012', '\166', '\147', '\113', '\040', '\156', + '\147', '\040', '\061', '\012', '\143', '\167', '\121', '\040', + '\143', '\150', '\040', '\061', '\012', '\107', '\156', '\170', + '\040', '\141', '\156', '\040', '\061', '\012', '\154', '\142', + '\120', '\040', '\154', '\145', '\040', '\061', '\012', '\153', + '\144', '\123', '\040', '\144', '\145', '\040', '\061', '\012', + '\153', '\104', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\120', '\166', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\171', '\110', '\163', '\040', '\163', '\164', + '\040', '\061', '\012', '\114', '\147', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\130', '\155', '\152', '\040', + '\151', '\152', '\040', '\061', '\012', '\160', '\166', '\101', + '\040', '\166', '\141', '\040', '\061', '\012', '\166', '\125', + '\165', '\040', '\161', '\165', '\040', '\061', '\012', '\121', + '\152', '\165', '\040', '\161', '\165', '\040', '\061', '\012', + '\161', '\104', '\146', '\040', '\161', '\165', '\040', '\061', + '\012', '\107', '\170', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\107', '\146', '\172', '\040', '\163', '\172', + '\040', '\061', '\012', '\147', '\142', '\131', '\040', '\156', + '\147', '\040', '\061', '\012', '\123', '\152', '\146', '\040', + '\151', '\152', '\040', '\061', '\012', '\117', '\147', '\167', + '\040', '\156', '\147', '\040', '\061', '\012', '\150', '\107', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\142', + '\164', '\124', '\040', '\164', '\150', '\040', '\061', '\012', + '\147', '\167', '\110', '\040', '\156', '\147', '\040', '\061', + '\012', '\115', '\167', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\146', '\166', '\125', '\040', '\166', '\141', + '\040', '\061', '\012', '\146', '\162', '\107', '\040', '\145', + '\162', '\040', '\061', '\012', '\143', '\115', '\170', '\040', + '\143', '\150', '\040', '\061', '\012', '\131', '\144', '\166', + '\040', '\144', '\145', '\040', '\061', '\012', '\170', '\153', + '\132', '\040', '\153', '\141', '\040', '\061', '\012', '\146', + '\152', '\114', '\040', '\151', '\152', '\040', '\061', '\012', + '\171', '\120', '\170', '\040', '\156', '\171', '\040', '\061', + '\012', '\144', '\162', '\130', '\040', '\145', '\162', '\040', + '\061', '\012', '\152', '\170', '\122', '\040', '\151', '\152', + '\040', '\061', '\012', '\150', '\131', '\161', '\040', '\164', + '\150', '\040', '\061', '\012', '\170', '\110', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\152', '\162', '\120', + '\040', '\145', '\162', '\040', '\061', '\012', '\164', '\143', + '\112', '\040', '\164', '\150', '\040', '\061', '\012', '\161', + '\112', '\172', '\040', '\161', '\165', '\040', '\061', '\012', + '\172', '\125', '\144', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\130', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\161', '\104', '\144', '\040', '\161', '\165', + '\040', '\061', '\012', '\102', '\152', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\161', '\106', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\155', '\170', '\107', + '\040', '\155', '\145', '\040', '\061', '\012', '\170', '\117', + '\144', '\040', '\144', '\145', '\040', '\061', '\012', '\150', + '\147', '\114', '\040', '\164', '\150', '\040', '\061', '\012', + '\143', '\160', '\104', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\150', '\123', '\040', '\164', '\150', '\040', + '\061', '\012', '\132', '\161', '\160', '\040', '\161', '\165', + '\040', '\061', '\012', '\171', '\116', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\160', '\110', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\162', '\132', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\127', '\152', + '\171', '\040', '\151', '\152', '\040', '\061', '\012', '\124', + '\146', '\142', '\040', '\142', '\145', '\040', '\061', '\012', + '\116', '\167', '\142', '\040', '\167', '\141', '\040', '\061', + '\012', '\172', '\121', '\153', '\040', '\163', '\172', '\040', + '\061', '\012', '\122', '\153', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\121', '\166', '\167', '\040', '\166', + '\141', '\040', '\061', '\012', '\167', '\154', '\112', '\040', + '\154', '\145', '\040', '\061', '\012', '\143', '\106', '\160', + '\040', '\143', '\150', '\040', '\061', '\012', '\157', '\104', + '\142', '\040', '\157', '\156', '\040', '\061', '\012', '\154', + '\163', '\131', '\040', '\154', '\145', '\040', '\061', '\012', + '\132', '\142', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\103', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\172', '\170', '\116', '\040', '\163', '\172', + '\040', '\061', '\012', '\142', '\121', '\146', '\040', '\142', + '\145', '\040', '\061', '\012', '\113', '\152', '\171', '\040', + '\151', '\152', '\040', '\061', '\012', '\117', '\166', '\153', + '\040', '\153', '\141', '\040', '\061', '\012', '\143', '\170', + '\101', '\040', '\143', '\150', '\040', '\061', '\012', '\110', + '\161', '\167', '\040', '\161', '\165', '\040', '\061', '\012', + '\150', '\167', '\131', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\107', '\166', '\040', '\163', '\164', '\040', + '\061', '\012', '\122', '\167', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\172', '\166', '\110', '\040', '\163', + '\172', '\040', '\061', '\012', '\171', '\126', '\167', '\040', + '\167', '\141', '\040', '\061', '\012', '\172', '\155', '\130', + '\040', '\163', '\172', '\040', '\061', '\012', '\161', '\144', + '\115', '\040', '\161', '\165', '\040', '\061', '\012', '\144', + '\112', '\166', '\040', '\144', '\145', '\040', '\061', '\012', + '\167', '\104', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\126', '\150', '\155', '\040', '\164', '\150', '\040', + '\061', '\012', '\146', '\114', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\142', '\166', '\103', '\040', '\166', + '\141', '\040', '\061', '\012', '\170', '\126', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\110', '\146', '\170', + '\040', '\146', '\157', '\040', '\061', '\012', '\164', '\121', + '\154', '\040', '\164', '\150', '\040', '\061', '\012', '\154', + '\150', '\127', '\040', '\164', '\150', '\040', '\061', '\012', + '\157', '\161', '\123', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\171', '\141', '\040', '\141', '\156', '\040', + '\061', '\012', '\147', '\132', '\146', '\040', '\156', '\147', + '\040', '\061', '\012', '\142', '\113', '\171', '\040', '\142', + '\145', '\040', '\061', '\012', '\164', '\152', '\130', '\040', + '\164', '\150', '\040', '\061', '\012', '\126', '\153', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\171', '\152', + '\166', '\040', '\151', '\152', '\040', '\061', '\012', '\142', + '\147', '\116', '\040', '\156', '\147', '\040', '\061', '\012', + '\154', '\116', '\155', '\040', '\154', '\145', '\040', '\061', + '\012', '\112', '\172', '\154', '\040', '\154', '\145', '\040', + '\061', '\012', '\114', '\167', '\170', '\040', '\167', '\141', + '\040', '\061', '\012', '\166', '\143', '\114', '\040', '\143', + '\150', '\040', '\061', '\012', '\171', '\130', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\172', '\164', '\132', + '\040', '\164', '\150', '\040', '\061', '\012', '\171', '\112', + '\170', '\040', '\156', '\171', '\040', '\061', '\012', '\156', + '\160', '\126', '\040', '\141', '\156', '\040', '\061', '\012', + '\163', '\167', '\107', '\040', '\163', '\164', '\040', '\061', + '\012', '\163', '\130', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\145', '\112', '\142', '\040', '\145', '\162', + '\040', '\061', '\012', '\144', '\143', '\122', '\040', '\143', + '\150', '\040', '\061', '\012', '\132', '\162', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\120', '\147', '\166', + '\040', '\156', '\147', '\040', '\061', '\012', '\170', '\131', + '\162', '\040', '\145', '\162', '\040', '\061', '\012', '\152', + '\154', '\111', '\040', '\154', '\145', '\040', '\061', '\012', + '\106', '\155', '\146', '\040', '\155', '\145', '\040', '\061', + '\012', '\107', '\161', '\153', '\040', '\161', '\165', '\040', + '\061', '\012', '\166', '\154', '\132', '\040', '\154', '\145', + '\040', '\061', '\012', '\103', '\163', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\165', '\121', '\152', '\040', + '\161', '\165', '\040', '\061', '\012', '\154', '\114', '\155', + '\040', '\154', '\145', '\040', '\061', '\012', '\150', '\167', + '\113', '\040', '\164', '\150', '\040', '\061', '\012', '\143', + '\121', '\166', '\040', '\143', '\150', '\040', '\061', '\012', + '\161', '\146', '\110', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\122', '\167', '\040', '\145', '\162', '\040', + '\061', '\012', '\141', '\125', '\157', '\040', '\141', '\156', + '\040', '\061', '\012', '\161', '\160', '\105', '\040', '\161', + '\165', '\040', '\061', '\012', '\154', '\120', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\144', '\110', '\144', + '\040', '\144', '\145', '\040', '\061', '\012', '\147', '\161', + '\114', '\040', '\161', '\165', '\040', '\061', '\012', '\172', + '\127', '\160', '\040', '\163', '\172', '\040', '\061', '\012', + '\142', '\102', '\161', '\040', '\142', '\145', '\040', '\061', + '\012', '\167', '\127', '\160', '\040', '\160', '\162', '\040', + '\061', '\012', '\143', '\146', '\113', '\040', '\143', '\150', + '\040', '\061', '\012', '\146', '\127', '\170', '\040', '\146', + '\157', '\040', '\061', '\012', '\162', '\166', '\126', '\040', + '\145', '\162', '\040', '\061', '\012', '\172', '\150', '\122', + '\040', '\164', '\150', '\040', '\061', '\012', '\113', '\154', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\143', + '\142', '\121', '\040', '\143', '\150', '\040', '\061', '\012', + '\112', '\155', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\120', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\121', '\156', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\163', '\115', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\141', '\106', '\172', '\040', + '\141', '\156', '\040', '\061', '\012', '\163', '\112', '\163', + '\040', '\163', '\164', '\040', '\061', '\012', '\120', '\167', + '\152', '\040', '\151', '\152', '\040', '\061', '\012', '\152', + '\143', '\114', '\040', '\143', '\150', '\040', '\061', '\012', + '\147', '\155', '\121', '\040', '\156', '\147', '\040', '\061', + '\012', '\131', '\161', '\162', '\040', '\161', '\165', '\040', + '\061', '\012', '\103', '\147', '\172', '\040', '\156', '\147', + '\040', '\061', '\012', '\167', '\161', '\172', '\040', '\161', + '\165', '\040', '\061', '\012', '\146', '\156', '\111', '\040', + '\156', '\164', '\040', '\061', '\012', '\161', '\117', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\166', '\171', + '\125', '\040', '\166', '\141', '\040', '\061', '\012', '\167', + '\121', '\172', '\040', '\163', '\172', '\040', '\061', '\012', + '\166', '\125', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\102', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\144', '\116', '\155', '\040', '\144', '\145', + '\040', '\061', '\012', '\105', '\167', '\170', '\040', '\167', + '\141', '\040', '\061', '\012', '\171', '\160', '\104', '\040', + '\160', '\162', '\040', '\061', '\012', '\167', '\170', '\114', + '\040', '\167', '\141', '\040', '\061', '\012', '\161', '\145', + '\116', '\040', '\161', '\165', '\040', '\061', '\012', '\166', + '\153', '\102', '\040', '\153', '\141', '\040', '\061', '\012', + '\152', '\102', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\147', '\125', '\152', '\040', '\156', '\147', '\040', + '\061', '\012', '\153', '\121', '\153', '\040', '\153', '\141', + '\040', '\061', '\012', '\146', '\167', '\117', '\040', '\167', + '\141', '\040', '\061', '\012', '\161', '\121', '\164', '\040', + '\164', '\150', '\040', '\061', '\012', '\121', '\162', '\154', + '\040', '\145', '\162', '\040', '\061', '\012', '\144', '\124', + '\170', '\040', '\144', '\145', '\040', '\061', '\012', '\146', + '\127', '\144', '\040', '\144', '\145', '\040', '\061', '\012', + '\152', '\170', '\113', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\110', '\154', '\040', '\154', '\145', '\040', + '\061', '\012', '\152', '\143', '\131', '\040', '\143', '\150', + '\040', '\061', '\012', '\157', '\112', '\163', '\040', '\157', + '\156', '\040', '\061', '\012', '\163', '\122', '\170', '\040', + '\163', '\164', '\040', '\061', '\012', '\165', '\121', '\147', + '\040', '\161', '\165', '\040', '\061', '\012', '\150', '\150', + '\131', '\040', '\164', '\150', '\040', '\061', '\012', '\163', + '\144', '\116', '\040', '\163', '\164', '\040', '\061', '\012', + '\155', '\170', '\122', '\040', '\155', '\145', '\040', '\061', + '\012', '\130', '\163', '\166', '\040', '\163', '\164', '\040', + '\061', '\012', '\120', '\143', '\161', '\040', '\143', '\150', + '\040', '\061', '\012', '\160', '\153', '\132', '\040', '\153', + '\141', '\040', '\061', '\012', '\172', '\104', '\154', '\040', + '\154', '\145', '\040', '\061', '\012', '\162', '\111', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\110', '\156', + '\166', '\040', '\141', '\156', '\040', '\061', '\012', '\152', + '\160', '\101', '\040', '\151', '\152', '\040', '\061', '\012', + '\150', '\132', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\156', '\144', '\040', '\141', '\156', '\040', + '\061', '\012', '\150', '\132', '\144', '\040', '\164', '\150', + '\040', '\061', '\012', '\161', '\162', '\117', '\040', '\161', + '\165', '\040', '\061', '\012', '\123', '\142', '\170', '\040', + '\142', '\145', '\040', '\061', '\012', '\164', '\127', '\160', + '\040', '\164', '\150', '\040', '\061', '\012', '\110', '\160', + '\144', '\040', '\144', '\145', '\040', '\061', '\012', '\110', + '\152', '\172', '\040', '\163', '\172', '\040', '\061', '\012', + '\172', '\143', '\123', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\120', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\110', '\164', '\161', '\040', '\164', '\150', + '\040', '\061', '\012', '\147', '\143', '\107', '\040', '\143', + '\150', '\040', '\061', '\012', '\130', '\161', '\170', '\040', + '\161', '\165', '\040', '\061', '\012', '\155', '\132', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\130', '\172', + '\166', '\040', '\163', '\172', '\040', '\061', '\012', '\113', + '\147', '\167', '\040', '\156', '\147', '\040', '\061', '\012', + '\141', '\125', '\146', '\040', '\141', '\156', '\040', '\061', + '\012', '\131', '\155', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\167', '\143', '\131', '\040', '\143', '\150', + '\040', '\061', '\012', '\157', '\126', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\160', '\144', '\115', '\040', + '\144', '\145', '\040', '\061', '\012', '\166', '\172', '\113', + '\040', '\163', '\172', '\040', '\061', '\012', '\154', '\162', + '\130', '\040', '\145', '\162', '\040', '\061', '\012', '\171', + '\144', '\126', '\040', '\144', '\145', '\040', '\061', '\012', + '\165', '\161', '\120', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\155', '\116', '\040', '\155', '\145', '\040', + '\061', '\012', '\117', '\143', '\147', '\040', '\143', '\150', + '\040', '\061', '\012', '\146', '\114', '\153', '\040', '\153', + '\141', '\040', '\061', '\012', '\143', '\112', '\163', '\040', + '\143', '\150', '\040', '\061', '\012', '\165', '\107', '\146', + '\040', '\161', '\165', '\040', '\061', '\012', '\143', '\115', + '\153', '\040', '\143', '\150', '\040', '\061', '\012', '\147', + '\124', '\170', '\040', '\156', '\147', '\040', '\061', '\012', + '\170', '\116', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\110', '\154', '\040', '\154', '\145', '\040', + '\061', '\012', '\165', '\127', '\160', '\040', '\161', '\165', + '\040', '\061', '\012', '\144', '\170', '\114', '\040', '\144', + '\145', '\040', '\061', '\012', '\172', '\170', '\107', '\040', + '\163', '\172', '\040', '\061', '\012', '\144', '\126', '\156', + '\040', '\141', '\156', '\040', '\061', '\012', '\116', '\142', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\103', + '\170', '\163', '\040', '\163', '\164', '\040', '\061', '\012', + '\143', '\166', '\107', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\103', '\146', '\040', '\167', '\141', '\040', + '\061', '\012', '\153', '\152', '\103', '\040', '\151', '\152', + '\040', '\061', '\012', '\143', '\146', '\131', '\040', '\143', + '\150', '\040', '\061', '\012', '\172', '\143', '\146', '\040', + '\143', '\150', '\040', '\061', '\012', '\144', '\160', '\127', + '\040', '\144', '\145', '\040', '\061', '\012', '\120', '\161', + '\171', '\040', '\161', '\165', '\040', '\061', '\012', '\164', + '\154', '\116', '\040', '\164', '\150', '\040', '\061', '\012', + '\163', '\111', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\161', '\170', '\103', '\040', '\161', '\165', '\040', + '\061', '\012', '\113', '\152', '\155', '\040', '\151', '\152', + '\040', '\061', '\012', '\172', '\132', '\153', '\040', '\163', + '\172', '\040', '\061', '\012', '\106', '\153', '\163', '\040', + '\163', '\164', '\040', '\061', '\012', '\147', '\127', '\142', + '\040', '\156', '\147', '\040', '\061', '\012', '\164', '\161', + '\113', '\040', '\164', '\150', '\040', '\061', '\012', '\112', + '\154', '\166', '\040', '\154', '\145', '\040', '\061', '\012', + '\153', '\103', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\167', '\150', '\124', '\040', '\164', '\150', '\040', + '\061', '\012', '\117', '\167', '\166', '\040', '\166', '\141', + '\040', '\061', '\012', '\172', '\113', '\155', '\040', '\163', + '\172', '\040', '\061', '\012', '\152', '\161', '\154', '\040', + '\161', '\165', '\040', '\061', '\012', '\164', '\107', '\172', + '\040', '\164', '\150', '\040', '\061', '\012', '\144', '\103', + '\167', '\040', '\144', '\145', '\040', '\061', '\012', '\171', + '\155', '\121', '\040', '\155', '\145', '\040', '\061', '\012', + '\170', '\156', '\106', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\165', '\106', '\040', '\161', '\165', '\040', + '\061', '\012', '\160', '\106', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\152', '\171', '\123', '\040', '\151', + '\152', '\040', '\061', '\012', '\160', '\152', '\130', '\040', + '\151', '\152', '\040', '\061', '\012', '\154', '\117', '\152', + '\040', '\154', '\145', '\040', '\061', '\012', '\112', '\155', + '\144', '\040', '\144', '\145', '\040', '\061', '\012', '\132', + '\166', '\172', '\040', '\163', '\172', '\040', '\061', '\012', + '\152', '\161', '\115', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\124', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\161', '\117', '\151', '\040', '\161', '\165', + '\040', '\061', '\012', '\157', '\112', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\115', '\152', '\170', '\040', + '\151', '\152', '\040', '\061', '\012', '\124', '\160', '\142', + '\040', '\160', '\162', '\040', '\061', '\012', '\127', '\164', + '\166', '\040', '\164', '\150', '\040', '\061', '\012', '\152', + '\170', '\117', '\040', '\151', '\152', '\040', '\061', '\012', + '\144', '\102', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\164', '\116', '\166', '\040', '\164', '\150', '\040', + '\061', '\012', '\161', '\124', '\142', '\040', '\161', '\165', + '\040', '\061', '\012', '\166', '\156', '\125', '\040', '\141', + '\156', '\040', '\061', '\012', '\172', '\104', '\170', '\040', + '\163', '\172', '\040', '\061', '\012', '\160', '\123', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\170', '\122', + '\155', '\040', '\155', '\145', '\040', '\061', '\012', '\161', + '\125', '\146', '\040', '\161', '\165', '\040', '\061', '\012', + '\155', '\102', '\142', '\040', '\155', '\145', '\040', '\061', + '\012', '\161', '\152', '\111', '\040', '\161', '\165', '\040', + '\061', '\012', '\163', '\111', '\171', '\040', '\163', '\164', + '\040', '\061', '\012', '\144', '\103', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\161', '\111', '\170', '\040', + '\161', '\165', '\040', '\061', '\012', '\160', '\132', '\160', + '\040', '\160', '\162', '\040', '\061', '\012', '\161', '\104', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\170', + '\162', '\115', '\040', '\145', '\162', '\040', '\061', '\012', + '\165', '\117', '\145', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\147', '\117', '\040', '\156', '\147', '\040', + '\061', '\012', '\147', '\162', '\130', '\040', '\156', '\147', + '\040', '\061', '\012', '\120', '\147', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\171', '\126', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\161', '\105', '\165', + '\040', '\165', '\156', '\040', '\061', '\012', '\153', '\102', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\123', + '\147', '\172', '\040', '\156', '\147', '\040', '\061', '\012', + '\150', '\152', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\117', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\160', '\155', '\127', '\040', '\155', '\145', + '\040', '\061', '\012', '\107', '\156', '\167', '\040', '\141', + '\156', '\040', '\061', '\012', '\170', '\132', '\154', '\040', + '\154', '\145', '\040', '\061', '\012', '\150', '\124', '\144', + '\040', '\164', '\150', '\040', '\061', '\012', '\107', '\146', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\163', + '\114', '\146', '\040', '\163', '\164', '\040', '\061', '\012', + '\120', '\147', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\164', '\167', '\106', '\040', '\164', '\150', '\040', + '\061', '\012', '\155', '\104', '\153', '\040', '\153', '\141', + '\040', '\061', '\012', '\161', '\144', '\131', '\040', '\161', + '\165', '\040', '\061', '\012', '\166', '\163', '\132', '\040', + '\163', '\164', '\040', '\061', '\012', '\166', '\143', '\103', + '\040', '\143', '\150', '\040', '\061', '\012', '\104', '\143', + '\152', '\040', '\143', '\150', '\040', '\061', '\012', '\167', + '\125', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\161', '\111', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\162', '\132', '\040', '\161', '\165', '\040', + '\061', '\012', '\143', '\142', '\123', '\040', '\143', '\150', + '\040', '\061', '\012', '\130', '\172', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\166', '\127', '\152', '\040', + '\151', '\152', '\040', '\061', '\012', '\160', '\166', '\103', + '\040', '\166', '\141', '\040', '\061', '\012', '\112', '\162', + '\167', '\040', '\145', '\162', '\040', '\061', '\012', '\171', + '\170', '\111', '\040', '\156', '\171', '\040', '\061', '\012', + '\144', '\161', '\111', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\103', '\155', '\040', '\161', '\165', '\040', + '\061', '\012', '\166', '\130', '\144', '\040', '\144', '\145', + '\040', '\061', '\012', '\127', '\144', '\160', '\040', '\144', + '\145', '\040', '\061', '\012', '\104', '\172', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\150', '\144', '\126', + '\040', '\164', '\150', '\040', '\061', '\012', '\161', '\142', + '\117', '\040', '\161', '\165', '\040', '\061', '\012', '\112', + '\167', '\153', '\040', '\153', '\141', '\040', '\061', '\012', + '\127', '\161', '\155', '\040', '\161', '\165', '\040', '\061', + '\012', '\151', '\130', '\167', '\040', '\151', '\156', '\040', + '\061', '\012', '\146', '\131', '\154', '\040', '\154', '\145', + '\040', '\061', '\012', '\161', '\165', '\121', '\040', '\165', + '\156', '\040', '\061', '\012', '\153', '\152', '\104', '\040', + '\151', '\152', '\040', '\061', '\012', '\155', '\111', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\170', '\127', + '\167', '\040', '\167', '\141', '\040', '\061', '\012', '\157', + '\103', '\167', '\040', '\157', '\156', '\040', '\061', '\012', + '\132', '\143', '\166', '\040', '\143', '\150', '\040', '\061', + '\012', '\152', '\144', '\116', '\040', '\144', '\145', '\040', + '\061', '\012', '\165', '\131', '\142', '\040', '\161', '\165', + '\040', '\061', '\012', '\123', '\162', '\170', '\040', '\145', + '\162', '\040', '\061', '\012', '\160', '\147', '\125', '\040', + '\156', '\147', '\040', '\061', '\012', '\162', '\121', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\155', '\110', + '\146', '\040', '\155', '\145', '\040', '\061', '\012', '\146', + '\102', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\152', '\126', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\131', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\126', '\147', '\152', '\040', '\156', '\147', + '\040', '\061', '\012', '\161', '\141', '\123', '\040', '\141', + '\156', '\040', '\061', '\012', '\160', '\170', '\127', '\040', + '\160', '\162', '\040', '\061', '\012', '\155', '\156', '\112', + '\040', '\141', '\156', '\040', '\061', '\012', '\102', '\167', + '\167', '\040', '\167', '\141', '\040', '\061', '\012', '\124', + '\161', '\172', '\040', '\161', '\165', '\040', '\061', '\012', + '\152', '\106', '\166', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\167', '\115', '\040', '\167', '\141', '\040', + '\061', '\012', '\104', '\161', '\167', '\040', '\161', '\165', + '\040', '\061', '\012', '\155', '\167', '\111', '\040', '\155', + '\145', '\040', '\061', '\012', '\166', '\150', '\127', '\040', + '\164', '\150', '\040', '\061', '\012', '\163', '\161', '\130', + '\040', '\161', '\165', '\040', '\061', '\012', '\164', '\154', + '\122', '\040', '\164', '\150', '\040', '\061', '\012', '\141', + '\102', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\161', '\156', '\132', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\130', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\163', '\103', '\152', '\040', '\163', '\164', + '\040', '\061', '\012', '\147', '\162', '\116', '\040', '\156', + '\147', '\040', '\061', '\012', '\164', '\131', '\166', '\040', + '\164', '\150', '\040', '\061', '\012', '\127', '\167', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\146', '\131', + '\151', '\040', '\151', '\156', '\040', '\061', '\012', '\142', + '\164', '\106', '\040', '\164', '\150', '\040', '\061', '\012', + '\167', '\121', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\132', '\154', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\143', '\112', '\172', '\040', '\143', '\150', + '\040', '\061', '\012', '\130', '\142', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\164', '\114', '\155', '\040', + '\164', '\150', '\040', '\061', '\012', '\132', '\154', '\170', + '\040', '\154', '\145', '\040', '\061', '\012', '\116', '\155', + '\152', '\040', '\151', '\152', '\040', '\061', '\012', '\150', + '\143', '\107', '\040', '\164', '\150', '\040', '\061', '\012', + '\127', '\162', '\153', '\040', '\145', '\162', '\040', '\061', + '\012', '\116', '\150', '\143', '\040', '\164', '\150', '\040', + '\061', '\012', '\166', '\161', '\104', '\040', '\161', '\165', + '\040', '\061', '\012', '\165', '\152', '\131', '\040', '\161', + '\165', '\040', '\061', '\012', '\151', '\112', '\144', '\040', + '\151', '\156', '\040', '\061', '\012', '\144', '\114', '\146', + '\040', '\144', '\145', '\040', '\061', '\012', '\143', '\121', + '\156', '\040', '\143', '\150', '\040', '\061', '\012', '\127', + '\146', '\170', '\040', '\146', '\157', '\040', '\061', '\012', + '\150', '\153', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\150', '\103', '\040', '\164', '\150', '\040', + '\061', '\012', '\172', '\115', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\172', '\114', '\172', '\040', '\163', + '\172', '\040', '\061', '\012', '\130', '\147', '\164', '\040', + '\164', '\150', '\040', '\061', '\012', '\161', '\113', '\162', + '\040', '\161', '\165', '\040', '\061', '\012', '\171', '\152', + '\112', '\040', '\151', '\152', '\040', '\061', '\012', '\162', + '\112', '\155', '\040', '\145', '\162', '\040', '\061', '\012', + '\126', '\170', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\102', '\170', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\143', '\156', '\121', '\040', '\143', '\150', + '\040', '\061', '\012', '\161', '\153', '\121', '\040', '\161', + '\165', '\040', '\061', '\012', '\116', '\154', '\167', '\040', + '\154', '\145', '\040', '\061', '\012', '\150', '\127', '\166', + '\040', '\164', '\150', '\040', '\061', '\012', '\167', '\144', + '\125', '\040', '\144', '\145', '\040', '\061', '\012', '\161', + '\164', '\102', '\040', '\164', '\150', '\040', '\061', '\012', + '\161', '\111', '\145', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\145', '\131', '\040', '\161', '\165', '\040', + '\061', '\012', '\132', '\162', '\160', '\040', '\145', '\162', + '\040', '\061', '\012', '\116', '\150', '\144', '\040', '\164', + '\150', '\040', '\061', '\012', '\146', '\104', '\160', '\040', + '\160', '\157', '\040', '\061', '\012', '\103', '\156', '\152', + '\040', '\141', '\156', '\040', '\061', '\012', '\153', '\170', + '\125', '\040', '\153', '\141', '\040', '\061', '\012', '\102', + '\161', '\166', '\040', '\161', '\165', '\040', '\061', '\012', + '\166', '\130', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\153', '\102', '\170', '\040', '\153', '\141', '\040', + '\061', '\012', '\146', '\102', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\160', '\115', '\170', '\040', '\160', + '\162', '\040', '\061', '\012', '\153', '\170', '\122', '\040', + '\153', '\141', '\040', '\061', '\012', '\114', '\172', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\152', '\102', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\106', + '\152', '\156', '\040', '\141', '\156', '\040', '\061', '\012', + '\167', '\160', '\103', '\040', '\160', '\162', '\040', '\061', + '\012', '\146', '\113', '\171', '\040', '\156', '\171', '\040', + '\061', '\012', '\150', '\167', '\104', '\040', '\164', '\150', + '\040', '\061', '\012', '\146', '\161', '\146', '\040', '\161', + '\165', '\040', '\061', '\012', '\161', '\102', '\171', '\040', + '\161', '\165', '\040', '\061', '\012', '\131', '\143', '\161', + '\040', '\143', '\150', '\040', '\061', '\012', '\116', '\156', + '\163', '\040', '\141', '\156', '\040', '\061', '\012', '\152', + '\155', '\132', '\040', '\151', '\152', '\040', '\061', '\012', + '\147', '\113', '\167', '\040', '\156', '\147', '\040', '\061', + '\012', '\144', '\161', '\101', '\040', '\161', '\165', '\040', + '\061', '\012', '\102', '\152', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\146', '\107', '\170', '\040', '\146', + '\157', '\040', '\061', '\012', '\114', '\156', '\160', '\040', + '\141', '\156', '\040', '\061', '\012', '\167', '\150', '\125', + '\040', '\164', '\150', '\040', '\061', '\012', '\161', '\120', + '\144', '\040', '\161', '\165', '\040', '\061', '\012', '\171', + '\115', '\170', '\040', '\156', '\171', '\040', '\061', '\012', + '\167', '\105', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\153', '\155', '\112', '\040', '\153', '\141', '\040', + '\061', '\012', '\121', '\163', '\170', '\040', '\163', '\164', + '\040', '\061', '\012', '\154', '\103', '\167', '\040', '\154', + '\145', '\040', '\061', '\012', '\121', '\161', '\142', '\040', + '\161', '\165', '\040', '\061', '\012', '\150', '\166', '\112', + '\040', '\164', '\150', '\040', '\061', '\012', '\170', '\153', + '\116', '\040', '\153', '\141', '\040', '\061', '\012', '\165', + '\126', '\147', '\040', '\161', '\165', '\040', '\061', '\012', + '\163', '\121', '\155', '\040', '\163', '\164', '\040', '\061', + '\012', '\165', '\112', '\160', '\040', '\161', '\165', '\040', + '\061', '\012', '\131', '\172', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\143', '\130', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\163', '\162', '\111', '\040', + '\145', '\162', '\040', '\061', '\012', '\164', '\102', '\172', + '\040', '\164', '\150', '\040', '\061', '\012', '\143', '\122', + '\152', '\040', '\143', '\150', '\040', '\061', '\012', '\171', + '\111', '\167', '\040', '\167', '\141', '\040', '\061', '\012', + '\152', '\110', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\170', '\106', '\160', '\040', '\160', '\162', '\040', + '\061', '\012', '\167', '\112', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\161', '\144', '\106', '\040', '\161', + '\165', '\040', '\061', '\012', '\166', '\113', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\163', '\110', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\150', '\102', + '\146', '\040', '\164', '\150', '\040', '\061', '\012', '\152', + '\104', '\171', '\040', '\151', '\152', '\040', '\061', '\012', + '\107', '\152', '\170', '\040', '\151', '\152', '\040', '\061', + '\012', '\106', '\153', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\110', '\150', '\172', '\040', '\164', '\150', + '\040', '\061', '\012', '\170', '\123', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\152', '\106', '\146', '\040', + '\151', '\152', '\040', '\061', '\012', '\161', '\166', '\115', + '\040', '\161', '\165', '\040', '\061', '\012', '\157', '\122', + '\167', '\040', '\157', '\156', '\040', '\061', '\012', '\170', + '\147', '\130', '\040', '\156', '\147', '\040', '\061', '\012', + '\147', '\152', '\106', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\104', '\172', '\040', '\161', '\165', '\040', + '\061', '\012', '\131', '\143', '\146', '\040', '\143', '\150', + '\040', '\061', '\012', '\130', '\143', '\167', '\040', '\143', + '\150', '\040', '\061', '\012', '\156', '\146', '\121', '\040', + '\141', '\156', '\040', '\061', '\012', '\161', '\107', '\163', + '\040', '\161', '\165', '\040', '\061', '\012', '\153', '\107', + '\163', '\040', '\163', '\164', '\040', '\061', '\012', '\146', + '\170', '\126', '\040', '\146', '\157', '\040', '\061', '\012', + '\151', '\120', '\152', '\040', '\151', '\156', '\040', '\061', + '\012', '\161', '\147', '\120', '\040', '\161', '\165', '\040', + '\061', '\012', '\152', '\111', '\166', '\040', '\151', '\152', + '\040', '\061', '\012', '\126', '\150', '\165', '\040', '\164', + '\150', '\040', '\061', '\012', '\102', '\172', '\152', '\040', + '\163', '\172', '\040', '\061', '\012', '\112', '\166', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\126', '\152', + '\146', '\040', '\151', '\152', '\040', '\061', '\012', '\167', + '\124', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\160', '\104', '\167', '\040', '\160', '\162', '\040', '\061', + '\012', '\131', '\163', '\166', '\040', '\163', '\164', '\040', + '\061', '\012', '\172', '\164', '\126', '\040', '\164', '\150', + '\040', '\061', '\012', '\155', '\164', '\132', '\040', '\164', + '\150', '\040', '\061', '\012', '\152', '\106', '\171', '\040', + '\151', '\152', '\040', '\061', '\012', '\147', '\161', '\103', + '\040', '\161', '\165', '\040', '\061', '\012', '\126', '\163', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\147', + '\152', '\123', '\040', '\156', '\147', '\040', '\061', '\012', + '\166', '\130', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\142', '\160', '\113', '\040', '\160', '\162', '\040', + '\061', '\012', '\156', '\104', '\161', '\040', '\141', '\156', + '\040', '\061', '\012', '\163', '\113', '\170', '\040', '\163', + '\164', '\040', '\061', '\012', '\170', '\131', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\146', '\132', '\144', + '\040', '\144', '\145', '\040', '\061', '\012', '\160', '\170', + '\146', '\040', '\160', '\162', '\040', '\061', '\012', '\152', + '\161', '\123', '\040', '\161', '\165', '\040', '\061', '\012', + '\150', '\124', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\116', '\153', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\161', '\160', '\110', '\040', '\161', '\165', + '\040', '\061', '\012', '\166', '\105', '\172', '\040', '\163', + '\172', '\040', '\061', '\012', '\166', '\161', '\120', '\040', + '\161', '\165', '\040', '\061', '\012', '\166', '\110', '\167', + '\040', '\166', '\141', '\040', '\061', '\012', '\104', '\153', + '\160', '\040', '\153', '\141', '\040', '\061', '\012', '\143', + '\161', '\131', '\040', '\143', '\150', '\040', '\061', '\012', + '\155', '\161', '\123', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\126', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\120', '\170', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\150', '\170', '\116', '\040', '\164', + '\150', '\040', '\061', '\012', '\171', '\124', '\146', '\040', + '\156', '\171', '\040', '\061', '\012', '\167', '\103', '\152', + '\040', '\151', '\152', '\040', '\061', '\012', '\161', '\121', + '\167', '\040', '\161', '\165', '\040', '\061', '\012', '\126', + '\146', '\166', '\040', '\166', '\141', '\040', '\061', '\012', + '\171', '\121', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\147', '\125', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\167', '\163', '\121', '\040', '\163', '\164', + '\040', '\061', '\012', '\146', '\107', '\167', '\040', '\167', + '\141', '\040', '\061', '\012', '\167', '\113', '\146', '\040', + '\167', '\141', '\040', '\061', '\012', '\167', '\167', '\102', + '\040', '\167', '\141', '\040', '\061', '\012', '\166', '\106', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\164', + '\167', '\121', '\040', '\164', '\150', '\040', '\061', '\012', + '\156', '\162', '\102', '\040', '\141', '\156', '\040', '\061', + '\012', '\154', '\160', '\131', '\040', '\154', '\145', '\040', + '\061', '\012', '\170', '\154', '\122', '\040', '\154', '\145', + '\040', '\061', '\012', '\146', '\144', '\113', '\040', '\144', + '\145', '\040', '\061', '\012', '\145', '\106', '\172', '\040', + '\145', '\162', '\040', '\061', '\012', '\152', '\171', '\121', + '\040', '\151', '\152', '\040', '\061', '\012', '\154', '\167', + '\124', '\040', '\154', '\145', '\040', '\061', '\012', '\170', + '\103', '\167', '\040', '\167', '\141', '\040', '\061', '\012', + '\143', '\147', '\115', '\040', '\143', '\150', '\040', '\061', + '\012', '\167', '\164', '\126', '\040', '\164', '\150', '\040', + '\061', '\012', '\141', '\161', '\112', '\040', '\141', '\156', + '\040', '\061', '\012', '\142', '\130', '\165', '\040', '\161', + '\165', '\040', '\061', '\012', '\161', '\144', '\121', '\040', + '\161', '\165', '\040', '\061', '\012', '\131', '\170', '\144', + '\040', '\144', '\145', '\040', '\061', '\012', '\170', '\143', + '\123', '\040', '\143', '\150', '\040', '\061', '\012', '\156', + '\155', '\126', '\040', '\141', '\156', '\040', '\061', '\012', + '\162', '\121', '\144', '\040', '\145', '\162', '\040', '\061', + '\012', '\107', '\154', '\153', '\040', '\154', '\145', '\040', + '\061', '\012', '\161', '\105', '\155', '\040', '\161', '\165', + '\040', '\061', '\012', '\165', '\166', '\117', '\040', '\161', + '\165', '\040', '\061', '\012', '\163', '\166', '\106', '\040', + '\163', '\164', '\040', '\061', '\012', '\163', '\112', '\170', + '\040', '\163', '\164', '\040', '\061', '\012', '\121', '\171', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\155', + '\130', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\142', '\164', '\104', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\107', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\146', '\132', '\157', '\040', '\157', '\156', + '\040', '\061', '\012', '\105', '\166', '\170', '\040', '\166', + '\141', '\040', '\061', '\012', '\166', '\172', '\104', '\040', + '\163', '\172', '\040', '\061', '\012', '\165', '\146', '\103', + '\040', '\161', '\165', '\040', '\061', '\012', '\120', '\170', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\161', + '\144', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\162', '\113', '\172', '\040', '\145', '\162', '\040', '\061', + '\012', '\112', '\150', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\103', '\170', '\153', '\040', '\153', '\141', + '\040', '\061', '\012', '\161', '\170', '\122', '\040', '\161', + '\165', '\040', '\061', '\012', '\147', '\124', '\154', '\040', + '\156', '\147', '\040', '\061', '\012', '\161', '\107', '\146', + '\040', '\161', '\165', '\040', '\061', '\012', '\167', '\131', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\143', + '\105', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\142', '\172', '\125', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\127', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\162', '\127', '\142', '\040', '\145', '\162', + '\040', '\061', '\012', '\127', '\162', '\160', '\040', '\145', + '\162', '\040', '\061', '\012', '\163', '\114', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\112', '\160', '\165', + '\040', '\161', '\165', '\040', '\061', '\012', '\112', '\153', + '\146', '\040', '\153', '\141', '\040', '\061', '\012', '\166', + '\147', '\105', '\040', '\156', '\147', '\040', '\061', '\012', + '\102', '\161', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\157', '\121', '\163', '\040', '\157', '\156', '\040', + '\061', '\012', '\153', '\142', '\132', '\040', '\153', '\141', + '\040', '\061', '\012', '\162', '\126', '\146', '\040', '\145', + '\162', '\040', '\061', '\012', '\161', '\114', '\167', '\040', + '\161', '\165', '\040', '\061', '\012', '\114', '\162', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\170', '\163', + '\122', '\040', '\163', '\164', '\040', '\061', '\012', '\150', + '\167', '\102', '\040', '\164', '\150', '\040', '\061', '\012', + '\121', '\156', '\153', '\040', '\141', '\156', '\040', '\061', + '\012', '\143', '\120', '\172', '\040', '\143', '\150', '\040', + '\061', '\012', '\125', '\143', '\161', '\040', '\143', '\150', + '\040', '\061', '\012', '\145', '\147', '\112', '\040', '\156', + '\147', '\040', '\061', '\012', '\121', '\171', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\130', '\167', '\162', + '\040', '\160', '\162', '\040', '\061', '\012', '\170', '\146', + '\104', '\040', '\146', '\157', '\040', '\061', '\012', '\167', + '\171', '\110', '\040', '\167', '\141', '\040', '\061', '\012', + '\154', '\102', '\167', '\040', '\154', '\145', '\040', '\061', + '\012', '\115', '\144', '\170', '\040', '\144', '\145', '\040', + '\061', '\012', '\121', '\163', '\171', '\040', '\163', '\164', + '\040', '\061', '\012', '\172', '\161', '\126', '\040', '\161', + '\165', '\040', '\061', '\012', '\166', '\160', '\131', '\040', + '\166', '\141', '\040', '\061', '\012', '\163', '\154', '\131', + '\040', '\154', '\145', '\040', '\061', '\012', '\167', '\147', + '\114', '\040', '\156', '\147', '\040', '\061', '\012', '\163', + '\156', '\116', '\040', '\141', '\156', '\040', '\061', '\012', + '\150', '\126', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\113', '\170', '\040', '\156', '\171', '\040', + '\061', '\012', '\142', '\144', '\127', '\040', '\144', '\145', + '\040', '\061', '\012', '\154', '\161', '\114', '\040', '\161', + '\165', '\040', '\061', '\012', '\171', '\150', '\104', '\040', + '\164', '\150', '\040', '\061', '\012', '\164', '\116', '\172', + '\040', '\164', '\150', '\040', '\061', '\012', '\172', '\112', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\153', + '\111', '\170', '\040', '\153', '\141', '\040', '\061', '\012', + '\146', '\110', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\171', '\162', '\112', '\040', '\145', '\162', '\040', + '\061', '\012', '\154', '\162', '\122', '\040', '\145', '\162', + '\040', '\061', '\012', '\167', '\172', '\131', '\040', '\163', + '\172', '\040', '\061', '\012', '\160', '\147', '\102', '\040', + '\160', '\162', '\040', '\061', '\012', '\155', '\146', '\103', + '\040', '\155', '\145', '\040', '\061', '\012', '\161', '\153', + '\114', '\040', '\161', '\165', '\040', '\061', '\012', '\152', + '\125', '\165', '\040', '\161', '\165', '\040', '\061', '\012', + '\161', '\103', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\154', '\116', '\040', '\154', '\145', '\040', + '\061', '\012', '\102', '\147', '\152', '\040', '\156', '\147', + '\040', '\061', '\012', '\147', '\143', '\105', '\040', '\143', + '\150', '\040', '\061', '\012', '\172', '\122', '\170', '\040', + '\163', '\172', '\040', '\061', '\012', '\152', '\150', '\116', + '\040', '\164', '\150', '\040', '\061', '\012', '\145', '\107', + '\172', '\040', '\145', '\162', '\040', '\061', '\012', '\106', + '\160', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\127', '\166', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\155', '\102', '\146', '\040', '\155', '\145', '\040', + '\061', '\012', '\150', '\150', '\127', '\040', '\164', '\150', + '\040', '\061', '\012', '\157', '\125', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\144', '\170', '\121', '\040', + '\144', '\145', '\040', '\061', '\012', '\127', '\150', '\161', + '\040', '\164', '\150', '\040', '\061', '\012', '\162', '\115', + '\153', '\040', '\145', '\162', '\040', '\061', '\012', '\154', + '\127', '\144', '\040', '\154', '\145', '\040', '\061', '\012', + '\170', '\127', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\157', '\121', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\155', '\127', '\170', '\040', '\155', '\145', + '\040', '\061', '\012', '\156', '\165', '\126', '\040', '\141', + '\156', '\040', '\061', '\012', '\167', '\127', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\150', '\166', '\122', + '\040', '\164', '\150', '\040', '\061', '\012', '\132', '\167', + '\144', '\040', '\144', '\145', '\040', '\061', '\012', '\163', + '\155', '\112', '\040', '\163', '\164', '\040', '\061', '\012', + '\110', '\154', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\112', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\172', '\155', '\131', '\040', '\163', '\172', + '\040', '\061', '\012', '\150', '\132', '\156', '\040', '\164', + '\150', '\040', '\061', '\012', '\126', '\152', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\112', '\150', '\172', + '\040', '\164', '\150', '\040', '\061', '\012', '\155', '\161', + '\122', '\040', '\161', '\165', '\040', '\061', '\012', '\150', + '\143', '\117', '\040', '\164', '\150', '\040', '\061', '\012', + '\144', '\161', '\114', '\040', '\161', '\165', '\040', '\061', + '\012', '\102', '\146', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\160', '\153', '\126', '\040', '\153', '\141', + '\040', '\061', '\012', '\164', '\102', '\170', '\040', '\164', + '\150', '\040', '\061', '\012', '\110', '\153', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\113', '\161', '\155', + '\040', '\161', '\165', '\040', '\061', '\012', '\161', '\127', + '\166', '\040', '\161', '\165', '\040', '\061', '\012', '\154', + '\130', '\171', '\040', '\154', '\145', '\040', '\061', '\012', + '\171', '\122', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\155', '\152', '\110', '\040', '\151', '\152', '\040', + '\061', '\012', '\161', '\172', '\101', '\040', '\161', '\165', + '\040', '\061', '\012', '\161', '\170', '\155', '\040', '\161', + '\165', '\040', '\061', '\012', '\121', '\166', '\155', '\040', + '\166', '\141', '\040', '\061', '\012', '\147', '\143', '\115', + '\040', '\143', '\150', '\040', '\061', '\012', '\170', '\161', + '\170', '\040', '\161', '\165', '\040', '\061', '\012', '\153', + '\113', '\166', '\040', '\153', '\141', '\040', '\061', '\012', + '\171', '\157', '\130', '\040', '\160', '\157', '\040', '\061', + '\012', '\170', '\162', '\124', '\040', '\145', '\162', '\040', + '\061', '\012', '\143', '\127', '\161', '\040', '\143', '\150', + '\040', '\061', '\012', '\152', '\161', '\127', '\040', '\161', + '\165', '\040', '\061', '\012', '\163', '\127', '\152', '\040', + '\163', '\164', '\040', '\061', '\012', '\123', '\144', '\167', + '\040', '\144', '\145', '\040', '\061', '\012', '\144', '\146', + '\122', '\040', '\144', '\145', '\040', '\061', '\012', '\113', + '\161', '\156', '\040', '\141', '\156', '\040', '\061', '\012', + '\107', '\152', '\144', '\040', '\144', '\157', '\040', '\061', + '\012', '\121', '\142', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\171', '\171', '\113', '\040', '\156', '\171', + '\040', '\061', '\012', '\170', '\155', '\130', '\040', '\155', + '\145', '\040', '\061', '\012', '\170', '\165', '\106', '\040', + '\161', '\165', '\040', '\061', '\012', '\171', '\126', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\161', '\157', + '\117', '\040', '\161', '\165', '\040', '\061', '\012', '\107', + '\154', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\115', '\153', '\170', '\040', '\153', '\141', '\040', '\061', + '\012', '\170', '\114', '\142', '\040', '\142', '\145', '\040', + '\061', '\012', '\147', '\115', '\162', '\040', '\156', '\147', + '\040', '\061', '\012', '\163', '\103', '\160', '\040', '\163', + '\164', '\040', '\061', '\012', '\142', '\107', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\143', '\130', '\157', + '\040', '\143', '\150', '\040', '\061', '\012', '\172', '\124', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\161', + '\153', '\103', '\040', '\161', '\165', '\040', '\061', '\012', + '\150', '\124', '\160', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\116', '\146', '\040', '\161', '\165', '\040', + '\061', '\012', '\155', '\130', '\153', '\040', '\153', '\141', + '\040', '\061', '\012', '\170', '\143', '\132', '\040', '\143', + '\150', '\040', '\061', '\012', '\152', '\126', '\155', '\040', + '\151', '\152', '\040', '\061', '\012', '\142', '\111', '\151', + '\040', '\151', '\156', '\040', '\061', '\012', '\161', '\156', + '\110', '\040', '\141', '\156', '\040', '\061', '\012', '\156', + '\167', '\103', '\040', '\141', '\156', '\040', '\061', '\012', + '\144', '\123', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\157', '\104', '\040', '\161', '\165', '\040', + '\061', '\012', '\164', '\104', '\170', '\040', '\164', '\150', + '\040', '\061', '\012', '\152', '\144', '\125', '\040', '\144', + '\145', '\040', '\061', '\012', '\130', '\155', '\167', '\040', + '\155', '\145', '\040', '\061', '\012', '\153', '\116', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\152', '\131', + '\162', '\040', '\145', '\162', '\040', '\061', '\012', '\131', + '\147', '\160', '\040', '\156', '\147', '\040', '\061', '\012', + '\142', '\154', '\112', '\040', '\154', '\145', '\040', '\061', + '\012', '\155', '\106', '\166', '\040', '\166', '\141', '\040', + '\061', '\012', '\123', '\170', '\162', '\040', '\145', '\162', + '\040', '\061', '\012', '\106', '\172', '\154', '\040', '\154', + '\145', '\040', '\061', '\012', '\152', '\124', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\143', '\111', '\160', + '\040', '\160', '\162', '\040', '\061', '\012', '\141', '\152', + '\131', '\040', '\141', '\156', '\040', '\061', '\012', '\171', + '\131', '\142', '\040', '\142', '\145', '\040', '\061', '\012', + '\162', '\113', '\142', '\040', '\145', '\162', '\040', '\061', + '\012', '\160', '\172', '\102', '\040', '\163', '\172', '\040', + '\061', '\012', '\145', '\111', '\171', '\040', '\145', '\162', + '\040', '\061', '\012', '\167', '\146', '\113', '\040', '\167', + '\141', '\040', '\061', '\012', '\106', '\155', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\165', '\146', '\114', + '\040', '\161', '\165', '\040', '\061', '\012', '\130', '\154', + '\155', '\040', '\154', '\145', '\040', '\061', '\012', '\103', + '\172', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\154', '\120', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\161', '\126', '\040', '\164', '\150', '\040', + '\061', '\012', '\167', '\106', '\171', '\040', '\167', '\141', + '\040', '\061', '\012', '\142', '\121', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\153', '\126', '\167', '\040', + '\153', '\141', '\040', '\061', '\012', '\156', '\115', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\143', '\103', + '\152', '\040', '\143', '\150', '\040', '\061', '\012', '\157', + '\145', '\105', '\040', '\145', '\162', '\040', '\061', '\012', + '\167', '\110', '\146', '\040', '\167', '\141', '\040', '\061', + '\012', '\146', '\116', '\146', '\040', '\146', '\157', '\040', + '\061', '\012', '\155', '\130', '\166', '\040', '\166', '\141', + '\040', '\061', '\012', '\116', '\153', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\152', '\127', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\172', '\106', '\152', + '\040', '\163', '\172', '\040', '\061', '\012', '\113', '\146', + '\170', '\040', '\146', '\157', '\040', '\061', '\012', '\142', + '\147', '\131', '\040', '\156', '\147', '\040', '\061', '\012', + '\154', '\131', '\172', '\040', '\154', '\145', '\040', '\061', + '\012', '\143', '\147', '\104', '\040', '\143', '\150', '\040', + '\061', '\012', '\160', '\147', '\115', '\040', '\156', '\147', + '\040', '\061', '\012', '\146', '\150', '\110', '\040', '\164', + '\150', '\040', '\061', '\012', '\152', '\162', '\104', '\040', + '\145', '\162', '\040', '\061', '\012', '\152', '\167', '\101', + '\040', '\151', '\152', '\040', '\061', '\012', '\152', '\171', + '\115', '\040', '\151', '\152', '\040', '\061', '\012', '\166', + '\172', '\103', '\040', '\163', '\172', '\040', '\061', '\012', + '\154', '\121', '\144', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\143', '\110', '\040', '\143', '\150', '\040', + '\061', '\012', '\154', '\142', '\130', '\040', '\154', '\145', + '\040', '\061', '\012', '\166', '\172', '\107', '\040', '\163', + '\172', '\040', '\061', '\012', '\155', '\123', '\162', '\040', + '\145', '\162', '\040', '\061', '\012', '\170', '\131', '\146', + '\040', '\146', '\157', '\040', '\061', '\012', '\161', '\147', + '\102', '\040', '\161', '\165', '\040', '\061', '\012', '\152', + '\131', '\153', '\040', '\151', '\152', '\040', '\061', '\012', + '\144', '\111', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\160', '\107', '\040', '\160', '\162', '\040', + '\061', '\012', '\150', '\126', '\153', '\040', '\164', '\150', + '\040', '\061', '\012', '\124', '\152', '\142', '\040', '\151', + '\152', '\040', '\061', '\012', '\172', '\166', '\120', '\040', + '\163', '\172', '\040', '\061', '\012', '\142', '\132', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\142', '\106', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\153', + '\146', '\125', '\040', '\153', '\141', '\040', '\061', '\012', + '\123', '\170', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\167', '\106', '\040', '\167', '\141', '\040', + '\061', '\012', '\121', '\167', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\146', '\127', '\142', '\040', '\142', + '\145', '\040', '\061', '\012', '\152', '\161', '\121', '\040', + '\151', '\152', '\040', '\061', '\012', '\126', '\146', '\170', + '\040', '\146', '\157', '\040', '\061', '\012', '\143', '\112', + '\152', '\040', '\143', '\150', '\040', '\061', '\012', '\172', + '\167', '\112', '\040', '\163', '\172', '\040', '\061', '\012', + '\170', '\102', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\104', '\144', '\155', '\040', '\144', '\145', '\040', + '\061', '\012', '\142', '\127', '\166', '\040', '\166', '\141', + '\040', '\061', '\012', '\172', '\160', '\107', '\040', '\163', + '\172', '\040', '\061', '\012', '\170', '\162', '\121', '\040', + '\145', '\162', '\040', '\061', '\012', '\150', '\143', '\123', + '\040', '\164', '\150', '\040', '\061', '\012', '\167', '\110', + '\156', '\040', '\141', '\156', '\040', '\061', '\012', '\150', + '\111', '\171', '\040', '\164', '\150', '\040', '\061', '\012', + '\131', '\170', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\163', '\144', '\103', '\040', '\163', '\164', '\040', + '\061', '\012', '\171', '\126', '\165', '\040', '\161', '\165', + '\040', '\061', '\012', '\161', '\152', '\146', '\040', '\161', + '\165', '\040', '\061', '\012', '\124', '\172', '\171', '\040', + '\163', '\172', '\040', '\061', '\012', '\106', '\146', '\156', + '\040', '\141', '\156', '\040', '\061', '\012', '\172', '\172', + '\130', '\040', '\163', '\172', '\040', '\061', '\012', '\110', + '\144', '\170', '\040', '\144', '\145', '\040', '\061', '\012', + '\147', '\114', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\131', '\161', '\147', '\040', '\161', '\165', '\040', + '\061', '\012', '\146', '\114', '\142', '\040', '\142', '\145', + '\040', '\061', '\012', '\154', '\121', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\166', '\152', '\107', '\040', + '\151', '\152', '\040', '\061', '\012', '\167', '\160', '\114', + '\040', '\160', '\162', '\040', '\061', '\012', '\143', '\112', + '\162', '\040', '\143', '\150', '\040', '\061', '\012', '\141', + '\112', '\161', '\040', '\141', '\156', '\040', '\061', '\012', + '\131', '\156', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\127', '\166', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\154', '\113', '\171', '\040', '\154', '\145', + '\040', '\061', '\012', '\145', '\131', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\153', '\170', '\114', '\040', + '\153', '\141', '\040', '\061', '\012', '\147', '\103', '\142', + '\040', '\156', '\147', '\040', '\061', '\012', '\163', '\122', + '\144', '\040', '\163', '\164', '\040', '\061', '\012', '\162', + '\115', '\144', '\040', '\145', '\162', '\040', '\061', '\012', + '\102', '\166', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\113', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\167', '\154', '\113', '\040', '\154', '\145', + '\040', '\061', '\012', '\155', '\104', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\172', '\153', '\112', '\040', + '\163', '\172', '\040', '\061', '\012', '\166', '\122', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\130', '\154', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\160', + '\122', '\153', '\040', '\153', '\141', '\040', '\061', '\012', + '\170', '\166', '\116', '\040', '\166', '\141', '\040', '\061', + '\012', '\156', '\170', '\111', '\040', '\141', '\156', '\040', + '\061', '\012', '\146', '\103', '\170', '\040', '\146', '\157', + '\040', '\061', '\012', '\131', '\142', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\105', '\142', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\142', '\153', '\116', + '\040', '\153', '\141', '\040', '\061', '\012', '\142', '\121', + '\171', '\040', '\142', '\145', '\040', '\061', '\012', '\162', + '\104', '\167', '\040', '\145', '\162', '\040', '\061', '\012', + '\144', '\152', '\112', '\040', '\144', '\145', '\040', '\061', + '\012', '\164', '\155', '\115', '\040', '\164', '\150', '\040', + '\061', '\012', '\156', '\167', '\110', '\040', '\141', '\156', + '\040', '\061', '\012', '\150', '\112', '\172', '\040', '\164', + '\150', '\040', '\061', '\012', '\154', '\143', '\115', '\040', + '\143', '\150', '\040', '\061', '\012', '\157', '\172', '\126', + '\040', '\157', '\156', '\040', '\061', '\012', '\155', '\114', + '\144', '\040', '\144', '\145', '\040', '\061', '\012', '\142', + '\113', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\145', '\132', '\146', '\040', '\145', '\162', '\040', '\061', + '\012', '\106', '\150', '\147', '\040', '\164', '\150', '\040', + '\061', '\012', '\132', '\143', '\152', '\040', '\143', '\150', + '\040', '\061', '\012', '\160', '\114', '\162', '\040', '\145', + '\162', '\040', '\061', '\012', '\167', '\161', '\163', '\040', + '\161', '\165', '\040', '\061', '\012', '\142', '\130', '\151', + '\040', '\151', '\156', '\040', '\061', '\012', '\164', '\147', + '\104', '\040', '\164', '\150', '\040', '\061', '\012', '\150', + '\121', '\143', '\040', '\164', '\150', '\040', '\061', '\012', + '\172', '\104', '\160', '\040', '\163', '\172', '\040', '\061', + '\012', '\157', '\104', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\163', '\147', '\115', '\040', '\156', '\147', + '\040', '\061', '\012', '\142', '\156', '\104', '\040', '\141', + '\156', '\040', '\061', '\012', '\147', '\110', '\160', '\040', + '\156', '\147', '\040', '\061', '\012', '\127', '\153', '\146', + '\040', '\153', '\141', '\040', '\061', '\012', '\161', '\111', + '\163', '\040', '\161', '\165', '\040', '\061', '\012', '\167', + '\114', '\144', '\040', '\144', '\145', '\040', '\061', '\012', + '\172', '\164', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\147', '\144', '\121', '\040', '\156', '\147', '\040', + '\061', '\012', '\167', '\103', '\155', '\040', '\157', '\167', + '\040', '\061', '\012', '\166', '\126', '\146', '\040', '\166', + '\141', '\040', '\061', '\012', '\112', '\155', '\167', '\040', + '\155', '\145', '\040', '\061', '\012', '\150', '\142', '\103', + '\040', '\164', '\150', '\040', '\061', '\012', '\163', '\162', + '\127', '\040', '\145', '\162', '\040', '\061', '\012', '\156', + '\170', '\116', '\040', '\141', '\156', '\040', '\061', '\012', + '\160', '\126', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\165', '\127', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\150', '\147', '\115', '\040', '\164', '\150', + '\040', '\061', '\012', '\154', '\102', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\167', '\125', '\157', '\040', + '\157', '\156', '\040', '\061', '\012', '\146', '\154', '\110', + '\040', '\154', '\145', '\040', '\061', '\012', '\171', '\127', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\152', + '\152', '\116', '\040', '\151', '\152', '\040', '\061', '\012', + '\125', '\167', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\156', '\131', '\152', '\040', '\141', '\156', '\040', + '\061', '\012', '\155', '\164', '\116', '\040', '\164', '\150', + '\040', '\061', '\012', '\120', '\147', '\160', '\040', '\156', + '\147', '\040', '\061', '\012', '\172', '\106', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\157', '\130', '\172', + '\040', '\157', '\156', '\040', '\061', '\012', '\151', '\103', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\114', + '\160', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\107', '\161', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\131', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\166', '\161', '\101', '\040', '\161', '\165', + '\040', '\061', '\012', '\126', '\150', '\143', '\040', '\164', + '\150', '\040', '\061', '\012', '\172', '\155', '\106', '\040', + '\163', '\172', '\040', '\061', '\012', '\102', '\160', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\112', '\146', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\157', + '\130', '\166', '\040', '\157', '\156', '\040', '\061', '\012', + '\154', '\147', '\130', '\040', '\156', '\147', '\040', '\061', + '\012', '\112', '\146', '\170', '\040', '\146', '\157', '\040', + '\061', '\012', '\172', '\160', '\123', '\040', '\163', '\172', + '\040', '\061', '\012', '\147', '\143', '\117', '\040', '\143', + '\150', '\040', '\061', '\012', '\170', '\167', '\121', '\040', + '\167', '\141', '\040', '\061', '\012', '\160', '\153', '\121', + '\040', '\153', '\141', '\040', '\061', '\012', '\167', '\117', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\127', + '\147', '\155', '\040', '\156', '\147', '\040', '\061', '\012', + '\143', '\117', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\116', '\146', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\160', '\161', '\116', '\040', '\161', '\165', + '\040', '\061', '\012', '\161', '\163', '\102', '\040', '\161', + '\165', '\040', '\061', '\012', '\171', '\144', '\110', '\040', + '\144', '\145', '\040', '\061', '\012', '\161', '\122', '\163', + '\040', '\161', '\165', '\040', '\061', '\012', '\171', '\153', + '\130', '\040', '\153', '\141', '\040', '\061', '\012', '\143', + '\104', '\161', '\040', '\143', '\150', '\040', '\061', '\012', + '\155', '\146', '\125', '\040', '\155', '\145', '\040', '\061', + '\012', '\170', '\172', '\115', '\040', '\163', '\172', '\040', + '\061', '\012', '\166', '\107', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\146', '\165', '\127', '\040', '\161', + '\165', '\040', '\061', '\012', '\154', '\161', '\107', '\040', + '\161', '\165', '\040', '\061', '\012', '\124', '\161', '\160', + '\040', '\161', '\165', '\040', '\061', '\012', '\172', '\166', + '\104', '\040', '\163', '\172', '\040', '\061', '\012', '\167', + '\127', '\142', '\040', '\167', '\141', '\040', '\061', '\012', + '\106', '\172', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\161', '\160', '\113', '\040', '\161', '\165', '\040', + '\061', '\012', '\157', '\171', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\147', '\121', '\145', '\040', '\156', + '\147', '\040', '\061', '\012', '\132', '\155', '\167', '\040', + '\155', '\145', '\040', '\061', '\012', '\161', '\131', '\160', + '\040', '\161', '\165', '\040', '\061', '\012', '\127', '\166', + '\146', '\040', '\166', '\141', '\040', '\061', '\012', '\141', + '\121', '\154', '\040', '\141', '\156', '\040', '\061', '\012', + '\157', '\161', '\117', '\040', '\161', '\165', '\040', '\061', + '\012', '\145', '\161', '\112', '\040', '\161', '\165', '\040', + '\061', '\012', '\156', '\166', '\124', '\040', '\141', '\156', + '\040', '\061', '\012', '\146', '\125', '\153', '\040', '\153', + '\141', '\040', '\061', '\012', '\151', '\142', '\110', '\040', + '\151', '\156', '\040', '\061', '\012', '\152', '\166', '\132', + '\040', '\151', '\152', '\040', '\061', '\012', '\127', '\167', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\154', + '\147', '\131', '\040', '\156', '\147', '\040', '\061', '\012', + '\145', '\106', '\160', '\040', '\145', '\162', '\040', '\061', + '\012', '\130', '\147', '\170', '\040', '\156', '\147', '\040', + '\061', '\012', '\146', '\131', '\163', '\040', '\163', '\164', + '\040', '\061', '\012', '\153', '\132', '\163', '\040', '\163', + '\164', '\040', '\061', '\012', '\166', '\160', '\104', '\040', + '\166', '\141', '\040', '\061', '\012', '\161', '\143', '\132', + '\040', '\143', '\150', '\040', '\061', '\012', '\102', '\161', + '\157', '\040', '\161', '\165', '\040', '\061', '\012', '\152', + '\114', '\142', '\040', '\151', '\152', '\040', '\061', '\012', + '\162', '\167', '\130', '\040', '\145', '\162', '\040', '\061', + '\012', '\146', '\171', '\113', '\040', '\156', '\171', '\040', + '\061', '\012', '\123', '\170', '\166', '\040', '\166', '\141', + '\040', '\061', '\012', '\163', '\170', '\132', '\040', '\163', + '\164', '\040', '\061', '\012', '\167', '\153', '\113', '\040', + '\153', '\141', '\040', '\061', '\012', '\171', '\112', '\160', + '\040', '\160', '\162', '\040', '\061', '\012', '\164', '\152', + '\124', '\040', '\164', '\150', '\040', '\061', '\012', '\161', + '\120', '\166', '\040', '\161', '\165', '\040', '\061', '\012', + '\171', '\132', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\122', '\162', '\155', '\040', '\145', '\162', '\040', + '\061', '\012', '\156', '\150', '\112', '\040', '\164', '\150', + '\040', '\061', '\012', '\166', '\161', '\112', '\040', '\161', + '\165', '\040', '\061', '\012', '\171', '\170', '\131', '\040', + '\156', '\171', '\040', '\061', '\012', '\166', '\163', '\105', + '\040', '\163', '\164', '\040', '\061', '\012', '\146', '\153', + '\113', '\040', '\153', '\141', '\040', '\061', '\012', '\146', + '\165', '\131', '\040', '\161', '\165', '\040', '\061', '\012', + '\172', '\121', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\130', '\166', '\162', '\040', '\145', '\162', '\040', + '\061', '\012', '\155', '\115', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\117', '\161', '\155', '\040', '\161', + '\165', '\040', '\061', '\012', '\104', '\170', '\163', '\040', + '\163', '\164', '\040', '\061', '\012', '\114', '\161', '\141', + '\040', '\141', '\156', '\040', '\061', '\012', '\127', '\156', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\152', + '\155', '\107', '\040', '\151', '\152', '\040', '\061', '\012', + '\127', '\161', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\155', '\150', '\124', '\040', '\164', '\150', '\040', + '\061', '\012', '\142', '\147', '\132', '\040', '\156', '\147', + '\040', '\061', '\012', '\166', '\155', '\117', '\040', '\166', + '\141', '\040', '\061', '\012', '\172', '\106', '\155', '\040', + '\163', '\172', '\040', '\061', '\012', '\113', '\150', '\153', + '\040', '\164', '\150', '\040', '\061', '\012', '\171', '\161', + '\102', '\040', '\161', '\165', '\040', '\061', '\012', '\156', + '\126', '\166', '\040', '\141', '\156', '\040', '\061', '\012', + '\122', '\146', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\155', '\114', '\040', '\163', '\172', '\040', + '\061', '\012', '\150', '\144', '\104', '\040', '\164', '\150', + '\040', '\061', '\012', '\156', '\127', '\160', '\040', '\141', + '\156', '\040', '\061', '\012', '\166', '\166', '\117', '\040', + '\166', '\141', '\040', '\061', '\012', '\144', '\131', '\160', + '\040', '\144', '\145', '\040', '\061', '\012', '\157', '\150', + '\130', '\040', '\164', '\150', '\040', '\061', '\012', '\161', + '\157', '\125', '\040', '\161', '\165', '\040', '\061', '\012', + '\162', '\152', '\102', '\040', '\145', '\162', '\040', '\061', + '\012', '\104', '\167', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\141', '\127', '\161', '\040', '\141', '\156', + '\040', '\061', '\012', '\143', '\154', '\104', '\040', '\143', + '\150', '\040', '\061', '\012', '\126', '\144', '\153', '\040', + '\144', '\145', '\040', '\061', '\012', '\164', '\167', '\115', + '\040', '\164', '\150', '\040', '\061', '\012', '\146', '\132', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\167', + '\121', '\160', '\040', '\160', '\162', '\040', '\061', '\012', + '\144', '\167', '\104', '\040', '\144', '\145', '\040', '\061', + '\012', '\151', '\131', '\166', '\040', '\151', '\156', '\040', + '\061', '\012', '\101', '\167', '\166', '\040', '\166', '\141', + '\040', '\061', '\012', '\160', '\147', '\107', '\040', '\156', + '\147', '\040', '\061', '\012', '\130', '\157', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\153', '\162', '\121', + '\040', '\145', '\162', '\040', '\061', '\012', '\126', '\170', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\154', + '\167', '\102', '\040', '\154', '\145', '\040', '\061', '\012', + '\120', '\170', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\112', '\167', '\146', '\040', '\167', '\141', '\040', + '\061', '\012', '\172', '\114', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\142', '\164', '\110', '\040', '\164', + '\150', '\040', '\061', '\012', '\160', '\167', '\131', '\040', + '\160', '\162', '\040', '\061', '\012', '\115', '\152', '\144', + '\040', '\144', '\145', '\040', '\061', '\012', '\130', '\162', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\161', + '\130', '\165', '\040', '\165', '\156', '\040', '\061', '\012', + '\105', '\161', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\102', '\160', '\171', '\040', '\160', '\162', '\040', + '\061', '\012', '\172', '\156', '\131', '\040', '\141', '\156', + '\040', '\061', '\012', '\122', '\161', '\144', '\040', '\161', + '\165', '\040', '\061', '\012', '\156', '\121', '\146', '\040', + '\141', '\156', '\040', '\061', '\012', '\132', '\166', '\167', + '\040', '\166', '\141', '\040', '\061', '\012', '\172', '\152', + '\117', '\040', '\163', '\172', '\040', '\061', '\012', '\167', + '\116', '\144', '\040', '\144', '\145', '\040', '\061', '\012', + '\154', '\111', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\115', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\107', '\161', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\154', '\115', '\146', '\040', '\154', + '\145', '\040', '\061', '\012', '\112', '\161', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\146', '\126', '\167', + '\040', '\167', '\141', '\040', '\061', '\012', '\161', '\166', + '\121', '\040', '\161', '\165', '\040', '\061', '\012', '\145', + '\110', '\153', '\040', '\145', '\162', '\040', '\061', '\012', + '\152', '\142', '\113', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\127', '\163', '\040', '\163', '\164', '\040', + '\061', '\012', '\161', '\124', '\153', '\040', '\161', '\165', + '\040', '\061', '\012', '\172', '\156', '\106', '\040', '\141', + '\156', '\040', '\061', '\012', '\171', '\170', '\117', '\040', + '\156', '\171', '\040', '\061', '\012', '\106', '\161', '\162', + '\040', '\161', '\165', '\040', '\061', '\012', '\156', '\106', + '\142', '\040', '\141', '\156', '\040', '\061', '\012', '\157', + '\104', '\160', '\040', '\157', '\156', '\040', '\061', '\012', + '\152', '\125', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\110', '\147', '\040', '\161', '\165', '\040', + '\061', '\012', '\147', '\107', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\161', '\120', '\163', '\040', '\161', + '\165', '\040', '\061', '\012', '\152', '\110', '\166', '\040', + '\151', '\152', '\040', '\061', '\012', '\111', '\167', '\152', + '\040', '\151', '\152', '\040', '\061', '\012', '\166', '\172', + '\126', '\040', '\163', '\172', '\040', '\061', '\012', '\171', + '\125', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\152', '\121', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\106', '\142', '\040', '\163', '\164', '\040', + '\061', '\012', '\114', '\166', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\172', '\124', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\142', '\166', '\113', '\040', + '\166', '\141', '\040', '\061', '\012', '\103', '\143', '\170', + '\040', '\143', '\150', '\040', '\061', '\012', '\152', '\171', + '\101', '\040', '\151', '\152', '\040', '\061', '\012', '\171', + '\105', '\152', '\040', '\151', '\152', '\040', '\061', '\012', + '\172', '\144', '\107', '\040', '\163', '\172', '\040', '\061', + '\012', '\164', '\161', '\124', '\040', '\164', '\150', '\040', + '\061', '\012', '\161', '\142', '\110', '\040', '\161', '\165', + '\040', '\061', '\012', '\156', '\110', '\144', '\040', '\141', + '\156', '\040', '\061', '\012', '\110', '\150', '\152', '\040', + '\164', '\150', '\040', '\061', '\012', '\152', '\126', '\142', + '\040', '\151', '\152', '\040', '\061', '\012', '\165', '\110', + '\167', '\040', '\165', '\156', '\040', '\061', '\012', '\132', + '\143', '\153', '\040', '\143', '\150', '\040', '\061', '\012', + '\147', '\120', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\170', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\167', '\110', '\163', '\040', '\163', '\164', + '\040', '\061', '\012', '\146', '\104', '\171', '\040', '\156', + '\171', '\040', '\061', '\012', '\164', '\154', '\126', '\040', + '\164', '\150', '\040', '\061', '\012', '\114', '\163', '\166', + '\040', '\163', '\164', '\040', '\061', '\012', '\172', '\166', + '\106', '\040', '\166', '\141', '\040', '\061', '\012', '\155', + '\161', '\170', '\040', '\161', '\165', '\040', '\061', '\012', + '\156', '\161', '\106', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\147', '\115', '\040', '\156', '\147', '\040', + '\061', '\012', '\147', '\171', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\147', '\162', '\112', '\040', '\156', + '\147', '\040', '\061', '\012', '\152', '\123', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\115', '\155', '\167', + '\040', '\155', '\145', '\040', '\061', '\012', '\103', '\147', + '\170', '\040', '\156', '\147', '\040', '\061', '\012', '\122', + '\154', '\162', '\040', '\145', '\162', '\040', '\061', '\012', + '\155', '\166', '\107', '\040', '\166', '\141', '\040', '\061', + '\012', '\146', '\165', '\101', '\040', '\161', '\165', '\040', + '\061', '\012', '\165', '\126', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\163', '\115', '\172', '\040', '\163', + '\164', '\040', '\061', '\012', '\167', '\127', '\162', '\040', + '\145', '\162', '\040', '\061', '\012', '\161', '\160', '\104', + '\040', '\161', '\165', '\040', '\061', '\012', '\150', '\121', + '\167', '\040', '\164', '\150', '\040', '\061', '\012', '\170', + '\102', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\146', '\143', '\127', '\040', '\143', '\150', '\040', '\061', + '\012', '\150', '\170', '\114', '\040', '\164', '\150', '\040', + '\061', '\012', '\162', '\146', '\113', '\040', '\145', '\162', + '\040', '\061', '\012', '\155', '\106', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\121', '\156', '\167', '\040', + '\141', '\156', '\040', '\061', '\012', '\164', '\152', '\102', + '\040', '\164', '\150', '\040', '\061', '\012', '\122', '\153', + '\170', '\040', '\153', '\141', '\040', '\061', '\012', '\163', + '\162', '\105', '\040', '\145', '\162', '\040', '\061', '\012', + '\144', '\162', '\107', '\040', '\145', '\162', '\040', '\061', + '\012', '\103', '\146', '\171', '\040', '\156', '\171', '\040', + '\061', '\012', '\171', '\132', '\167', '\040', '\167', '\141', + '\040', '\061', '\012', '\127', '\170', '\167', '\040', '\167', + '\141', '\040', '\061', '\012', '\172', '\103', '\160', '\040', + '\163', '\172', '\040', '\061', '\012', '\152', '\132', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\116', '\161', + '\146', '\040', '\161', '\165', '\040', '\061', '\012', '\152', + '\147', '\117', '\040', '\156', '\147', '\040', '\061', '\012', + '\146', '\127', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\162', '\116', '\040', '\161', '\165', '\040', + '\061', '\012', '\116', '\172', '\152', '\040', '\163', '\172', + '\040', '\061', '\012', '\110', '\152', '\171', '\040', '\151', + '\152', '\040', '\061', '\012', '\125', '\170', '\171', '\040', + '\156', '\171', '\040', '\061', '\012', '\157', '\111', '\171', + '\040', '\157', '\156', '\040', '\061', '\012', '\162', '\146', + '\130', '\040', '\145', '\162', '\040', '\061', '\012', '\157', + '\102', '\167', '\040', '\157', '\156', '\040', '\061', '\012', + '\171', '\171', '\126', '\040', '\156', '\171', '\040', '\061', + '\012', '\121', '\151', '\166', '\040', '\151', '\156', '\040', + '\061', '\012', '\144', '\113', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\161', '\104', '\153', '\040', '\161', + '\165', '\040', '\061', '\012', '\164', '\147', '\121', '\040', + '\164', '\150', '\040', '\061', '\012', '\170', '\116', '\167', + '\040', '\167', '\141', '\040', '\061', '\012', '\161', '\144', + '\114', '\040', '\161', '\165', '\040', '\061', '\012', '\157', + '\166', '\131', '\040', '\157', '\156', '\040', '\061', '\012', + '\146', '\142', '\132', '\040', '\142', '\145', '\040', '\061', + '\012', '\161', '\151', '\111', '\040', '\161', '\165', '\040', + '\061', '\012', '\142', '\166', '\124', '\040', '\166', '\141', + '\040', '\061', '\012', '\152', '\131', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\153', '\142', '\113', '\040', + '\153', '\141', '\040', '\061', '\012', '\115', '\146', '\156', + '\040', '\141', '\156', '\040', '\061', '\012', '\122', '\160', + '\144', '\040', '\144', '\145', '\040', '\061', '\012', '\160', + '\110', '\142', '\040', '\160', '\162', '\040', '\061', '\012', + '\161', '\161', '\117', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\153', '\126', '\040', '\153', '\141', '\040', + '\061', '\012', '\163', '\127', '\160', '\040', '\163', '\164', + '\040', '\061', '\012', '\153', '\120', '\146', '\040', '\153', + '\141', '\040', '\061', '\012', '\161', '\114', '\171', '\040', + '\161', '\165', '\040', '\061', '\012', '\161', '\157', '\105', + '\040', '\161', '\165', '\040', '\061', '\012', '\167', '\114', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\172', + '\150', '\126', '\040', '\164', '\150', '\040', '\061', '\012', + '\142', '\160', '\114', '\040', '\160', '\162', '\040', '\061', + '\012', '\124', '\161', '\146', '\040', '\161', '\165', '\040', + '\061', '\012', '\160', '\172', '\107', '\040', '\163', '\172', + '\040', '\061', '\012', '\153', '\143', '\124', '\040', '\143', + '\150', '\040', '\061', '\012', '\167', '\152', '\130', '\040', + '\151', '\152', '\040', '\061', '\012', '\153', '\120', '\171', + '\040', '\153', '\165', '\040', '\061', '\012', '\146', '\144', + '\102', '\040', '\144', '\145', '\040', '\061', '\012', '\121', + '\170', '\163', '\040', '\163', '\164', '\040', '\061', '\012', + '\147', '\131', '\146', '\040', '\156', '\147', '\040', '\061', + '\012', '\131', '\160', '\170', '\040', '\160', '\162', '\040', + '\061', '\012', '\172', '\123', '\153', '\040', '\163', '\172', + '\040', '\061', '\012', '\164', '\104', '\147', '\040', '\164', + '\150', '\040', '\061', '\012', '\170', '\142', '\112', '\040', + '\142', '\145', '\040', '\061', '\012', '\171', '\146', '\117', + '\040', '\156', '\171', '\040', '\061', '\012', '\165', '\121', + '\146', '\040', '\161', '\165', '\040', '\061', '\012', '\142', + '\160', '\121', '\040', '\160', '\162', '\040', '\061', '\012', + '\144', '\130', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\154', '\167', '\120', '\040', '\154', '\145', '\040', + '\061', '\012', '\166', '\124', '\163', '\040', '\163', '\164', + '\040', '\061', '\012', '\112', '\154', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\103', '\161', '\167', '\040', + '\161', '\165', '\040', '\061', '\012', '\142', '\127', '\171', + '\040', '\142', '\145', '\040', '\061', '\012', '\143', '\125', + '\161', '\040', '\143', '\150', '\040', '\061', '\012', '\131', + '\142', '\153', '\040', '\153', '\141', '\040', '\061', '\012', + '\167', '\171', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\152', '\150', '\161', '\040', '\164', '\150', '\040', + '\061', '\012', '\170', '\125', '\171', '\040', '\156', '\171', + '\040', '\061', '\012', '\116', '\143', '\152', '\040', '\143', + '\150', '\040', '\061', '\012', '\153', '\115', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\166', '\132', '\171', + '\040', '\166', '\141', '\040', '\061', '\012', '\172', '\143', + '\161', '\040', '\143', '\150', '\040', '\061', '\012', '\121', + '\163', '\162', '\040', '\145', '\162', '\040', '\061', '\012', + '\114', '\150', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\107', '\143', '\152', '\040', '\143', '\150', '\040', + '\061', '\012', '\165', '\121', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\167', '\131', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\144', '\131', '\155', '\040', + '\144', '\145', '\040', '\061', '\012', '\121', '\166', '\170', + '\040', '\166', '\141', '\040', '\061', '\012', '\122', '\143', + '\147', '\040', '\143', '\150', '\040', '\061', '\012', '\161', + '\107', '\172', '\040', '\161', '\165', '\040', '\061', '\012', + '\142', '\170', '\112', '\040', '\142', '\145', '\040', '\061', + '\012', '\152', '\106', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\170', '\114', '\160', '\040', '\160', '\162', + '\040', '\061', '\012', '\154', '\104', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\167', '\161', '\123', '\040', + '\161', '\165', '\040', '\061', '\012', '\142', '\111', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\164', '\102', + '\155', '\040', '\164', '\150', '\040', '\061', '\012', '\142', + '\121', '\163', '\040', '\163', '\164', '\040', '\061', '\012', + '\172', '\112', '\142', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\146', '\112', '\040', '\151', '\152', '\040', + '\061', '\012', '\161', '\124', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\153', '\142', '\130', '\040', '\153', + '\141', '\040', '\061', '\012', '\110', '\154', '\172', '\040', + '\154', '\145', '\040', '\061', '\012', '\160', '\165', '\121', + '\040', '\161', '\165', '\040', '\061', '\012', '\150', '\113', + '\142', '\040', '\164', '\150', '\040', '\061', '\012', '\162', + '\102', '\142', '\040', '\145', '\162', '\040', '\061', '\012', + '\166', '\160', '\127', '\040', '\166', '\141', '\040', '\061', + '\012', '\131', '\152', '\153', '\040', '\151', '\152', '\040', + '\061', '\012', '\127', '\156', '\155', '\040', '\141', '\156', + '\040', '\061', '\012', '\160', '\132', '\162', '\040', '\145', + '\162', '\040', '\061', '\012', '\154', '\144', '\132', '\040', + '\154', '\145', '\040', '\061', '\012', '\147', '\115', '\155', + '\040', '\156', '\147', '\040', '\061', '\012', '\160', '\132', + '\146', '\040', '\160', '\151', '\040', '\061', '\012', '\145', + '\131', '\160', '\040', '\145', '\162', '\040', '\061', '\012', + '\166', '\124', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\107', '\153', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\103', '\147', '\171', '\040', '\156', '\147', + '\040', '\061', '\012', '\161', '\104', '\167', '\040', '\161', + '\165', '\040', '\061', '\012', '\147', '\170', '\127', '\040', + '\156', '\147', '\040', '\061', '\012', '\103', '\167', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\152', '\150', + '\131', '\040', '\164', '\150', '\040', '\061', '\012', '\106', + '\166', '\153', '\040', '\153', '\141', '\040', '\061', '\012', + '\156', '\146', '\110', '\040', '\141', '\156', '\040', '\061', + '\012', '\172', '\143', '\127', '\040', '\143', '\150', '\040', + '\061', '\012', '\172', '\147', '\103', '\040', '\156', '\147', + '\040', '\061', '\012', '\104', '\146', '\153', '\040', '\153', + '\141', '\040', '\061', '\012', '\166', '\160', '\112', '\040', + '\166', '\141', '\040', '\061', '\012', '\127', '\160', '\152', + '\040', '\151', '\152', '\040', '\061', '\012', '\163', '\103', + '\142', '\040', '\163', '\164', '\040', '\061', '\012', '\146', + '\147', '\106', '\040', '\156', '\147', '\040', '\061', '\012', + '\164', '\120', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\157', '\103', '\160', '\040', '\157', '\156', '\040', + '\061', '\012', '\116', '\162', '\170', '\040', '\145', '\162', + '\040', '\061', '\012', '\110', '\167', '\155', '\040', '\155', + '\145', '\040', '\061', '\012', '\146', '\122', '\160', '\040', + '\160', '\162', '\040', '\061', '\012', '\141', '\145', '\130', + '\040', '\141', '\156', '\040', '\061', '\012', '\152', '\144', + '\111', '\040', '\144', '\145', '\040', '\061', '\012', '\163', + '\102', '\166', '\040', '\163', '\164', '\040', '\061', '\012', + '\166', '\117', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\147', '\121', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\127', '\155', '\153', '\040', '\153', '\141', + '\040', '\061', '\012', '\120', '\161', '\152', '\040', '\161', + '\165', '\040', '\061', '\012', '\153', '\150', '\126', '\040', + '\164', '\150', '\040', '\061', '\012', '\110', '\153', '\152', + '\040', '\151', '\152', '\040', '\061', '\012', '\150', '\142', + '\102', '\040', '\164', '\150', '\040', '\061', '\012', '\166', + '\172', '\106', '\040', '\163', '\172', '\040', '\061', '\012', + '\131', '\142', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\163', '\130', '\142', '\040', '\163', '\164', '\040', + '\061', '\012', '\171', '\121', '\162', '\040', '\145', '\162', + '\040', '\061', '\012', '\150', '\150', '\126', '\040', '\164', + '\150', '\040', '\061', '\012', '\164', '\147', '\127', '\040', + '\164', '\150', '\040', '\061', '\012', '\142', '\130', '\157', + '\040', '\157', '\156', '\040', '\061', '\012', '\116', '\170', + '\160', '\040', '\160', '\162', '\040', '\061', '\012', '\141', + '\117', '\170', '\040', '\141', '\156', '\040', '\061', '\012', + '\172', '\146', '\142', '\040', '\163', '\172', '\040', '\061', + '\012', '\121', '\170', '\160', '\040', '\160', '\162', '\040', + '\061', '\012', '\161', '\167', '\121', '\040', '\161', '\165', + '\040', '\061', '\012', '\146', '\152', '\126', '\040', '\151', + '\152', '\040', '\061', '\012', '\150', '\152', '\131', '\040', + '\151', '\152', '\040', '\061', '\012', '\167', '\164', '\130', + '\040', '\164', '\150', '\040', '\061', '\012', '\152', '\147', + '\125', '\040', '\156', '\147', '\040', '\061', '\012', '\156', + '\115', '\161', '\040', '\141', '\156', '\040', '\061', '\012', + '\116', '\167', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\166', '\120', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\130', '\146', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\171', '\106', '\146', '\040', '\156', + '\171', '\040', '\061', '\012', '\146', '\110', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\156', '\132', '\146', + '\040', '\141', '\156', '\040', '\061', '\012', '\152', '\120', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\112', + '\147', '\142', '\040', '\156', '\147', '\040', '\061', '\012', + '\170', '\102', '\142', '\040', '\142', '\151', '\040', '\061', + '\012', '\163', '\152', '\117', '\040', '\163', '\164', '\040', + '\061', '\012', '\167', '\104', '\170', '\040', '\167', '\141', + '\040', '\061', '\012', '\156', '\152', '\116', '\040', '\141', + '\156', '\040', '\061', '\012', '\157', '\150', '\106', '\040', + '\164', '\150', '\040', '\061', '\012', '\160', '\161', '\122', + '\040', '\161', '\165', '\040', '\061', '\012', '\106', '\172', + '\167', '\040', '\163', '\172', '\040', '\061', '\012', '\161', + '\162', '\125', '\040', '\161', '\165', '\040', '\061', '\012', + '\143', '\152', '\107', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\106', '\166', '\040', '\153', '\141', '\040', + '\061', '\012', '\172', '\121', '\144', '\040', '\163', '\172', + '\040', '\061', '\012', '\166', '\142', '\105', '\040', '\166', + '\151', '\040', '\061', '\012', '\125', '\152', '\164', '\040', + '\164', '\150', '\040', '\061', '\012', '\161', '\111', '\142', + '\040', '\161', '\165', '\040', '\061', '\012', '\143', '\106', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\142', + '\166', '\131', '\040', '\166', '\141', '\040', '\061', '\012', + '\123', '\172', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\154', '\110', '\040', '\154', '\145', '\040', + '\061', '\012', '\161', '\143', '\131', '\040', '\143', '\150', + '\040', '\061', '\012', '\147', '\105', '\167', '\040', '\156', + '\147', '\040', '\061', '\012', '\170', '\150', '\114', '\040', + '\164', '\150', '\040', '\061', '\012', '\153', '\126', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\142', '\146', + '\110', '\040', '\142', '\145', '\040', '\061', '\012', '\116', + '\162', '\172', '\040', '\145', '\162', '\040', '\061', '\012', + '\163', '\112', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\142', '\127', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\156', '\166', '\113', '\040', '\141', '\156', + '\040', '\061', '\012', '\161', '\151', '\110', '\040', '\161', + '\165', '\040', '\061', '\012', '\161', '\142', '\123', '\040', + '\161', '\165', '\040', '\061', '\012', '\166', '\170', '\102', + '\040', '\166', '\141', '\040', '\061', '\012', '\164', '\166', + '\124', '\040', '\164', '\150', '\040', '\061', '\012', '\116', + '\162', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\154', '\131', '\170', '\040', '\154', '\145', '\040', '\061', + '\012', '\164', '\153', '\130', '\040', '\164', '\150', '\040', + '\061', '\012', '\107', '\172', '\170', '\040', '\163', '\172', + '\040', '\061', '\012', '\166', '\103', '\170', '\040', '\166', + '\151', '\040', '\061', '\012', '\132', '\142', '\152', '\040', + '\151', '\152', '\040', '\061', '\012', '\155', '\127', '\160', + '\040', '\155', '\145', '\040', '\061', '\012', '\104', '\161', + '\170', '\040', '\161', '\165', '\040', '\061', '\012', '\160', + '\146', '\105', '\040', '\160', '\162', '\040', '\061', '\012', + '\150', '\166', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\105', '\157', '\170', '\040', '\157', '\156', '\040', + '\061', '\012', '\144', '\142', '\132', '\040', '\144', '\145', + '\040', '\061', '\012', '\154', '\116', '\142', '\040', '\154', + '\145', '\040', '\061', '\012', '\162', '\124', '\144', '\040', + '\145', '\162', '\040', '\061', '\012', '\154', '\152', '\121', + '\040', '\154', '\145', '\040', '\061', '\012', '\126', '\166', + '\160', '\040', '\166', '\141', '\040', '\061', '\012', '\147', + '\112', '\167', '\040', '\156', '\147', '\040', '\061', '\012', + '\165', '\161', '\127', '\040', '\161', '\165', '\040', '\061', + '\012', '\107', '\152', '\146', '\040', '\151', '\152', '\040', + '\061', '\012', '\160', '\104', '\144', '\040', '\144', '\145', + '\040', '\061', '\012', '\163', '\147', '\121', '\040', '\156', + '\147', '\040', '\061', '\012', '\150', '\153', '\121', '\040', + '\164', '\150', '\040', '\061', '\012', '\146', '\112', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\155', '\144', + '\111', '\040', '\144', '\145', '\040', '\061', '\012', '\107', + '\143', '\160', '\040', '\143', '\150', '\040', '\061', '\012', + '\160', '\130', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\121', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\142', '\147', '\105', '\040', '\156', '\147', + '\040', '\061', '\012', '\113', '\172', '\166', '\040', '\163', + '\172', '\040', '\061', '\012', '\143', '\120', '\142', '\040', + '\143', '\150', '\040', '\061', '\012', '\110', '\143', '\172', + '\040', '\143', '\150', '\040', '\061', '\012', '\144', '\152', + '\121', '\040', '\144', '\145', '\040', '\061', '\012', '\160', + '\107', '\144', '\040', '\144', '\145', '\040', '\061', '\012', + '\146', '\171', '\105', '\040', '\156', '\171', '\040', '\061', + '\012', '\144', '\102', '\142', '\040', '\144', '\145', '\040', + '\061', '\012', '\145', '\120', '\152', '\040', '\145', '\162', + '\040', '\061', '\012', '\146', '\147', '\117', '\040', '\156', + '\147', '\040', '\061', '\012', '\170', '\122', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\170', '\161', '\113', + '\040', '\161', '\165', '\040', '\061', '\012', '\160', '\113', + '\160', '\040', '\160', '\162', '\040', '\061', '\012', '\170', + '\155', '\131', '\040', '\155', '\145', '\040', '\061', '\012', + '\150', '\147', '\117', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\144', '\107', '\040', '\144', '\145', '\040', + '\061', '\012', '\150', '\166', '\132', '\040', '\164', '\150', + '\040', '\061', '\012', '\163', '\162', '\106', '\040', '\145', + '\162', '\040', '\061', '\012', '\102', '\166', '\146', '\040', + '\166', '\151', '\040', '\061', '\012', '\171', '\166', '\104', + '\040', '\166', '\141', '\040', '\061', '\012', '\170', '\126', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\146', + '\131', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\142', '\161', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\145', '\106', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\143', '\167', '\132', '\040', '\143', '\150', + '\040', '\061', '\012', '\143', '\161', '\107', '\040', '\143', + '\150', '\040', '\061', '\012', '\163', '\113', '\160', '\040', + '\163', '\164', '\040', '\061', '\012', '\150', '\112', '\161', + '\040', '\164', '\150', '\040', '\061', '\012', '\166', '\114', + '\144', '\040', '\144', '\145', '\040', '\061', '\012', '\150', + '\144', '\113', '\040', '\164', '\150', '\040', '\061', '\012', + '\160', '\143', '\116', '\040', '\143', '\150', '\040', '\061', + '\012', '\164', '\116', '\146', '\040', '\164', '\150', '\040', + '\061', '\012', '\170', '\154', '\113', '\040', '\154', '\145', + '\040', '\061', '\012', '\162', '\112', '\170', '\040', '\145', + '\162', '\040', '\061', '\012', '\161', '\141', '\116', '\040', + '\141', '\156', '\040', '\061', '\012', '\172', '\113', '\146', + '\040', '\163', '\172', '\040', '\061', '\012', '\163', '\116', + '\146', '\040', '\163', '\164', '\040', '\061', '\012', '\161', + '\120', '\172', '\040', '\161', '\165', '\040', '\061', '\012', + '\142', '\172', '\114', '\040', '\163', '\172', '\040', '\061', + '\012', '\112', '\144', '\167', '\040', '\144', '\145', '\040', + '\061', '\012', '\156', '\122', '\142', '\040', '\141', '\156', + '\040', '\061', '\012', '\152', '\116', '\163', '\040', '\163', + '\164', '\040', '\061', '\012', '\164', '\156', '\126', '\040', + '\164', '\150', '\040', '\061', '\012', '\171', '\156', '\111', + '\040', '\141', '\156', '\040', '\061', '\012', '\164', '\132', + '\160', '\040', '\164', '\150', '\040', '\061', '\012', '\146', + '\132', '\160', '\040', '\160', '\162', '\040', '\061', '\012', + '\167', '\115', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\117', '\156', '\161', '\040', '\141', '\156', '\040', + '\061', '\012', '\172', '\111', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\142', '\166', '\110', '\040', '\166', + '\141', '\040', '\061', '\012', '\125', '\166', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\172', '\170', '\112', + '\040', '\163', '\172', '\040', '\061', '\012', '\126', '\155', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\165', + '\120', '\155', '\040', '\161', '\165', '\040', '\061', '\012', + '\155', '\167', '\104', '\040', '\155', '\145', '\040', '\061', + '\012', '\152', '\121', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\147', '\120', '\153', '\040', '\156', '\147', + '\040', '\061', '\012', '\166', '\146', '\126', '\040', '\166', + '\141', '\040', '\061', '\012', '\124', '\161', '\154', '\040', + '\161', '\165', '\040', '\061', '\012', '\142', '\112', '\154', + '\040', '\154', '\145', '\040', '\061', '\012', '\154', '\167', + '\117', '\040', '\154', '\145', '\040', '\061', '\012', '\167', + '\142', '\107', '\040', '\167', '\141', '\040', '\061', '\012', + '\146', '\124', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\130', '\164', '\161', '\040', '\164', '\150', '\040', + '\061', '\012', '\150', '\172', '\130', '\040', '\164', '\150', + '\040', '\061', '\012', '\120', '\172', '\166', '\040', '\163', + '\172', '\040', '\061', '\012', '\120', '\155', '\170', '\040', + '\155', '\145', '\040', '\061', '\012', '\170', '\132', '\155', + '\040', '\155', '\145', '\040', '\061', '\012', '\152', '\103', + '\160', '\040', '\151', '\152', '\040', '\061', '\012', '\142', + '\113', '\155', '\040', '\155', '\145', '\040', '\061', '\012', + '\124', '\155', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\110', '\156', '\146', '\040', '\141', '\156', '\040', + '\061', '\012', '\153', '\152', '\130', '\040', '\151', '\152', + '\040', '\061', '\012', '\166', '\147', '\110', '\040', '\156', + '\147', '\040', '\061', '\012', '\146', '\123', '\155', '\040', + '\155', '\145', '\040', '\061', '\012', '\171', '\154', '\116', + '\040', '\154', '\145', '\040', '\061', '\012', '\147', '\166', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\152', + '\124', '\172', '\040', '\163', '\172', '\040', '\061', '\012', + '\164', '\127', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\167', '\102', '\040', '\167', '\141', '\040', + '\061', '\012', '\142', '\103', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\144', '\116', '\153', '\040', '\144', + '\145', '\040', '\061', '\012', '\171', '\103', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\122', '\170', '\152', + '\040', '\151', '\152', '\040', '\061', '\012', '\156', '\124', + '\161', '\040', '\141', '\156', '\040', '\061', '\012', '\147', + '\106', '\163', '\040', '\156', '\147', '\040', '\061', '\012', + '\130', '\167', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\112', '\154', '\040', '\156', '\147', '\040', + '\061', '\012', '\166', '\143', '\122', '\040', '\143', '\150', + '\040', '\061', '\012', '\146', '\142', '\124', '\040', '\142', + '\145', '\040', '\061', '\012', '\106', '\143', '\144', '\040', + '\143', '\150', '\040', '\061', '\012', '\127', '\170', '\155', + '\040', '\155', '\145', '\040', '\061', '\012', '\161', '\167', + '\166', '\040', '\161', '\165', '\040', '\061', '\012', '\123', + '\146', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\154', '\143', '\113', '\040', '\143', '\150', '\040', '\061', + '\012', '\163', '\142', '\126', '\040', '\163', '\164', '\040', + '\061', '\012', '\146', '\123', '\146', '\040', '\146', '\157', + '\040', '\061', '\012', '\154', '\142', '\102', '\040', '\154', + '\145', '\040', '\061', '\012', '\117', '\143', '\167', '\040', + '\143', '\150', '\040', '\061', '\012', '\152', '\147', '\115', + '\040', '\156', '\147', '\040', '\061', '\012', '\156', '\142', + '\111', '\040', '\141', '\156', '\040', '\061', '\012', '\161', + '\163', '\113', '\040', '\161', '\165', '\040', '\061', '\012', + '\130', '\171', '\146', '\040', '\156', '\171', '\040', '\061', + '\012', '\160', '\170', '\166', '\040', '\166', '\141', '\040', + '\061', '\012', '\155', '\122', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\117', '\147', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\172', '\165', '\131', '\040', + '\161', '\165', '\040', '\061', '\012', '\146', '\130', '\165', + '\040', '\161', '\165', '\040', '\061', '\012', '\127', '\142', + '\152', '\040', '\151', '\152', '\040', '\061', '\012', '\124', + '\142', '\167', '\040', '\167', '\141', '\040', '\061', '\012', + '\172', '\162', '\122', '\040', '\145', '\162', '\040', '\061', + '\012', '\147', '\155', '\120', '\040', '\156', '\147', '\040', + '\061', '\012', '\143', '\103', '\155', '\040', '\143', '\150', + '\040', '\061', '\012', '\147', '\164', '\121', '\040', '\164', + '\150', '\040', '\061', '\012', '\160', '\150', '\107', '\040', + '\164', '\150', '\040', '\061', '\012', '\161', '\152', '\126', + '\040', '\161', '\165', '\040', '\061', '\012', '\171', '\147', + '\107', '\040', '\156', '\147', '\040', '\061', '\012', '\167', + '\106', '\142', '\040', '\167', '\141', '\040', '\061', '\012', + '\162', '\161', '\114', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\123', '\170', '\040', '\161', '\165', '\040', + '\061', '\012', '\171', '\142', '\113', '\040', '\142', '\145', + '\040', '\061', '\012', '\155', '\161', '\112', '\040', '\161', + '\165', '\040', '\061', '\012', '\121', '\162', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\161', '\144', '\111', + '\040', '\161', '\165', '\040', '\061', '\012', '\142', '\143', + '\107', '\040', '\143', '\150', '\040', '\061', '\012', '\151', + '\106', '\142', '\040', '\151', '\156', '\040', '\061', '\012', + '\155', '\143', '\132', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\103', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\170', '\110', '\172', '\040', '\164', '\172', + '\040', '\061', '\012', '\150', '\152', '\115', '\040', '\164', + '\150', '\040', '\061', '\012', '\161', '\164', '\114', '\040', + '\164', '\150', '\040', '\061', '\012', '\164', '\155', '\110', + '\040', '\164', '\150', '\040', '\061', '\012', '\163', '\154', + '\104', '\040', '\154', '\145', '\040', '\061', '\012', '\166', + '\122', '\172', '\040', '\163', '\172', '\040', '\061', '\012', + '\147', '\103', '\144', '\040', '\156', '\147', '\040', '\061', + '\012', '\130', '\170', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\161', '\113', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\163', '\111', '\167', '\040', '\163', + '\164', '\040', '\061', '\012', '\146', '\163', '\131', '\040', + '\163', '\164', '\040', '\061', '\012', '\170', '\162', '\112', + '\040', '\145', '\162', '\040', '\061', '\012', '\164', '\116', + '\163', '\040', '\164', '\150', '\040', '\061', '\012', '\147', + '\142', '\104', '\040', '\156', '\147', '\040', '\061', '\012', + '\167', '\114', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\150', '\106', '\146', '\040', '\164', '\150', '\040', + '\061', '\012', '\116', '\170', '\151', '\040', '\151', '\156', + '\040', '\061', '\012', '\146', '\122', '\142', '\040', '\142', + '\145', '\040', '\061', '\012', '\112', '\162', '\142', '\040', + '\145', '\162', '\040', '\061', '\012', '\152', '\105', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\150', '\167', + '\115', '\040', '\164', '\150', '\040', '\061', '\012', '\165', + '\126', '\167', '\040', '\161', '\165', '\040', '\061', '\012', + '\146', '\147', '\116', '\040', '\156', '\147', '\040', '\061', + '\012', '\155', '\101', '\157', '\040', '\157', '\156', '\040', + '\061', '\012', '\120', '\152', '\142', '\040', '\151', '\152', + '\040', '\061', '\012', '\156', '\160', '\120', '\040', '\151', + '\156', '\040', '\061', '\012', '\112', '\143', '\171', '\040', + '\143', '\150', '\040', '\061', '\012', '\171', '\112', '\142', + '\040', '\142', '\151', '\040', '\061', '\012', '\152', '\170', + '\111', '\040', '\151', '\152', '\040', '\061', '\012', '\113', + '\153', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\153', '\167', '\126', '\040', '\153', '\141', '\040', '\061', + '\012', '\147', '\122', '\146', '\040', '\156', '\147', '\040', + '\061', '\012', '\127', '\146', '\155', '\040', '\155', '\145', + '\040', '\061', '\012', '\124', '\144', '\160', '\040', '\160', + '\157', '\040', '\061', '\012', '\167', '\105', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\114', '\166', '\153', + '\040', '\153', '\141', '\040', '\061', '\012', '\104', '\161', + '\156', '\040', '\141', '\156', '\040', '\061', '\012', '\164', + '\161', '\114', '\040', '\164', '\150', '\040', '\061', '\012', + '\152', '\112', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\144', '\103', '\040', '\144', '\145', '\040', + '\061', '\012', '\150', '\170', '\125', '\040', '\164', '\150', + '\040', '\061', '\012', '\170', '\125', '\145', '\040', '\145', + '\162', '\040', '\061', '\012', '\164', '\121', '\143', '\040', + '\164', '\150', '\040', '\061', '\012', '\114', '\172', '\153', + '\040', '\163', '\172', '\040', '\061', '\012', '\144', '\124', + '\152', '\040', '\144', '\145', '\040', '\061', '\012', '\124', + '\154', '\172', '\040', '\154', '\145', '\040', '\061', '\012', + '\170', '\121', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\106', '\143', '\161', '\040', '\143', '\150', '\040', + '\061', '\012', '\167', '\147', '\105', '\040', '\156', '\147', + '\040', '\061', '\012', '\103', '\153', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\171', '\113', '\163', '\040', + '\163', '\164', '\040', '\061', '\012', '\170', '\167', '\123', + '\040', '\167', '\141', '\040', '\061', '\012', '\167', '\122', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\147', + '\153', '\113', '\040', '\156', '\147', '\040', '\061', '\012', + '\150', '\121', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\114', '\160', '\040', '\163', '\164', '\040', + '\061', '\012', '\152', '\101', '\151', '\040', '\151', '\156', + '\040', '\061', '\012', '\144', '\155', '\107', '\040', '\144', + '\145', '\040', '\061', '\012', '\152', '\113', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\161', '\125', '\142', + '\040', '\161', '\165', '\040', '\061', '\012', '\167', '\130', + '\171', '\040', '\167', '\141', '\040', '\061', '\012', '\142', + '\172', '\112', '\040', '\163', '\172', '\040', '\061', '\012', + '\147', '\172', '\112', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\116', '\172', '\040', '\164', '\150', '\040', + '\061', '\012', '\171', '\147', '\131', '\040', '\156', '\147', + '\040', '\061', '\012', '\161', '\150', '\125', '\040', '\164', + '\150', '\040', '\061', '\012', '\141', '\146', '\130', '\040', + '\141', '\156', '\040', '\061', '\012', '\152', '\132', '\167', + '\040', '\151', '\152', '\040', '\061', '\012', '\130', '\144', + '\170', '\040', '\144', '\145', '\040', '\061', '\012', '\124', + '\144', '\170', '\040', '\144', '\145', '\040', '\061', '\012', + '\152', '\116', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\130', '\146', '\040', '\166', '\141', '\040', + '\061', '\012', '\161', '\143', '\105', '\040', '\143', '\150', + '\040', '\061', '\012', '\115', '\156', '\167', '\040', '\141', + '\156', '\040', '\061', '\012', '\161', '\104', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\124', '\144', '\152', + '\040', '\144', '\145', '\040', '\061', '\012', '\144', '\147', + '\112', '\040', '\156', '\147', '\040', '\061', '\012', '\163', + '\144', '\122', '\040', '\163', '\164', '\040', '\061', '\012', + '\161', '\107', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\115', '\152', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\163', '\170', '\110', '\040', '\163', '\164', + '\040', '\061', '\012', '\120', '\160', '\172', '\040', '\163', + '\172', '\040', '\061', '\012', '\147', '\146', '\126', '\040', + '\156', '\147', '\040', '\061', '\012', '\146', '\117', '\171', + '\040', '\156', '\171', '\040', '\061', '\012', '\116', '\166', + '\170', '\040', '\166', '\151', '\040', '\061', '\012', '\161', + '\141', '\126', '\040', '\141', '\156', '\040', '\061', '\012', + '\170', '\152', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\147', '\132', '\040', '\156', '\147', '\040', + '\061', '\012', '\143', '\107', '\166', '\040', '\143', '\150', + '\040', '\061', '\012', '\132', '\170', '\165', '\040', '\161', + '\165', '\040', '\061', '\012', '\115', '\146', '\160', '\040', + '\160', '\162', '\040', '\061', '\012', '\172', '\106', '\160', + '\040', '\163', '\172', '\040', '\061', '\012', '\152', '\147', + '\112', '\040', '\156', '\147', '\040', '\061', '\012', '\142', + '\160', '\107', '\040', '\160', '\162', '\040', '\061', '\012', + '\166', '\113', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\161', '\111', '\040', '\164', '\150', '\040', + '\061', '\012', '\121', '\147', '\167', '\040', '\156', '\147', + '\040', '\061', '\012', '\121', '\171', '\171', '\040', '\156', + '\171', '\040', '\061', '\012', '\152', '\155', '\111', '\040', + '\151', '\152', '\040', '\061', '\012', '\126', '\147', '\144', + '\040', '\156', '\147', '\040', '\061', '\012', '\170', '\103', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\171', + '\126', '\163', '\040', '\163', '\164', '\040', '\061', '\012', + '\165', '\105', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\143', '\116', '\040', '\143', '\150', '\040', + '\061', '\012', '\102', '\172', '\142', '\040', '\163', '\172', + '\040', '\061', '\012', '\147', '\126', '\154', '\040', '\156', + '\147', '\040', '\061', '\012', '\163', '\130', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\153', '\121', '\146', + '\040', '\153', '\141', '\040', '\061', '\012', '\154', '\162', + '\131', '\040', '\145', '\162', '\040', '\061', '\012', '\126', + '\164', '\144', '\040', '\164', '\150', '\040', '\061', '\012', + '\156', '\110', '\163', '\040', '\141', '\156', '\040', '\061', + '\012', '\167', '\152', '\116', '\040', '\151', '\152', '\040', + '\061', '\012', '\162', '\172', '\112', '\040', '\145', '\162', + '\040', '\061', '\012', '\163', '\131', '\171', '\040', '\163', + '\164', '\040', '\061', '\012', '\167', '\170', '\121', '\040', + '\167', '\141', '\040', '\061', '\012', '\132', '\164', '\142', + '\040', '\164', '\150', '\040', '\061', '\012', '\164', '\127', + '\146', '\040', '\164', '\150', '\040', '\061', '\012', '\164', + '\103', '\170', '\040', '\164', '\150', '\040', '\061', '\012', + '\141', '\106', '\142', '\040', '\141', '\156', '\040', '\061', + '\012', '\154', '\161', '\146', '\040', '\161', '\165', '\040', + '\061', '\012', '\146', '\145', '\132', '\040', '\145', '\162', + '\040', '\061', '\012', '\146', '\120', '\172', '\040', '\163', + '\172', '\040', '\061', '\012', '\143', '\152', '\131', '\040', + '\143', '\150', '\040', '\061', '\012', '\167', '\113', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\121', '\150', + '\171', '\040', '\164', '\150', '\040', '\061', '\012', '\144', + '\103', '\152', '\040', '\144', '\145', '\040', '\061', '\012', + '\142', '\153', '\110', '\040', '\153', '\141', '\040', '\061', + '\012', '\171', '\152', '\104', '\040', '\151', '\152', '\040', + '\061', '\012', '\152', '\124', '\163', '\040', '\163', '\164', + '\040', '\061', '\012', '\150', '\170', '\111', '\040', '\164', + '\150', '\040', '\061', '\012', '\154', '\166', '\113', '\040', + '\166', '\151', '\040', '\061', '\012', '\114', '\167', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\163', '\167', + '\121', '\040', '\163', '\164', '\040', '\061', '\012', '\144', + '\124', '\153', '\040', '\144', '\151', '\040', '\061', '\012', + '\146', '\163', '\117', '\040', '\163', '\164', '\040', '\061', + '\012', '\154', '\152', '\105', '\040', '\154', '\145', '\040', + '\061', '\012', '\167', '\152', '\115', '\040', '\151', '\152', + '\040', '\061', '\012', '\165', '\121', '\153', '\040', '\161', + '\165', '\040', '\061', '\012', '\170', '\120', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\166', '\155', '\103', + '\040', '\166', '\141', '\040', '\061', '\012', '\161', '\163', + '\104', '\040', '\161', '\165', '\040', '\061', '\012', '\147', + '\104', '\167', '\040', '\156', '\147', '\040', '\061', '\012', + '\167', '\112', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\132', '\160', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\131', '\150', '\147', '\040', '\164', '\150', + '\040', '\061', '\012', '\153', '\116', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\142', '\127', '\154', '\040', + '\154', '\145', '\040', '\061', '\012', '\106', '\167', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\146', '\110', + '\170', '\040', '\146', '\157', '\040', '\061', '\012', '\106', + '\156', '\166', '\040', '\141', '\156', '\040', '\061', '\012', + '\146', '\144', '\114', '\040', '\144', '\145', '\040', '\061', + '\012', '\157', '\161', '\104', '\040', '\161', '\165', '\040', + '\061', '\012', '\141', '\131', '\170', '\040', '\141', '\156', + '\040', '\061', '\012', '\126', '\161', '\170', '\040', '\161', + '\165', '\040', '\061', '\012', '\166', '\113', '\146', '\040', + '\166', '\141', '\040', '\061', '\012', '\103', '\142', '\167', + '\040', '\167', '\141', '\040', '\061', '\012', '\166', '\171', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\143', + '\161', '\132', '\040', '\143', '\150', '\040', '\061', '\012', + '\122', '\146', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\123', '\167', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\161', '\116', '\151', '\040', '\161', '\165', + '\040', '\061', '\012', '\161', '\157', '\127', '\040', '\161', + '\165', '\040', '\061', '\012', '\152', '\150', '\104', '\040', + '\164', '\150', '\040', '\061', '\012', '\153', '\112', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\147', '\144', + '\106', '\040', '\156', '\147', '\040', '\061', '\012', '\160', + '\166', '\106', '\040', '\166', '\141', '\040', '\061', '\012', + '\143', '\160', '\126', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\164', '\103', '\040', '\164', '\150', '\040', + '\061', '\012', '\147', '\127', '\155', '\040', '\156', '\147', + '\040', '\061', '\012', '\147', '\120', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\152', '\102', '\163', '\040', + '\163', '\164', '\040', '\061', '\012', '\162', '\154', '\126', + '\040', '\145', '\162', '\040', '\061', '\012', '\147', '\132', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\153', + '\124', '\153', '\040', '\153', '\141', '\040', '\061', '\012', + '\150', '\146', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\123', '\166', '\166', '\040', '\166', '\141', '\040', + '\061', '\012', '\153', '\155', '\107', '\040', '\153', '\141', + '\040', '\061', '\012', '\163', '\104', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\150', '\107', '\142', '\040', + '\164', '\150', '\040', '\061', '\012', '\102', '\154', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\121', '\162', + '\171', '\040', '\145', '\162', '\040', '\061', '\012', '\150', + '\110', '\172', '\040', '\164', '\150', '\040', '\061', '\012', + '\171', '\114', '\170', '\040', '\156', '\171', '\040', '\061', + '\012', '\154', '\161', '\106', '\040', '\161', '\165', '\040', + '\061', '\012', '\167', '\142', '\102', '\040', '\142', '\151', + '\040', '\061', '\012', '\151', '\131', '\162', '\040', '\151', + '\156', '\040', '\061', '\012', '\167', '\104', '\172', '\040', + '\164', '\172', '\040', '\061', '\012', '\170', '\163', '\112', + '\040', '\163', '\164', '\040', '\061', '\012', '\142', '\172', + '\131', '\040', '\163', '\172', '\040', '\061', '\012', '\160', + '\115', '\167', '\040', '\160', '\162', '\040', '\061', '\012', + '\125', '\165', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\170', '\113', '\040', '\164', '\150', '\040', + '\061', '\012', '\130', '\166', '\146', '\040', '\166', '\141', + '\040', '\061', '\012', '\153', '\162', '\132', '\040', '\145', + '\162', '\040', '\061', '\012', '\146', '\167', '\126', '\040', + '\167', '\141', '\040', '\061', '\012', '\147', '\120', '\167', + '\040', '\156', '\147', '\040', '\061', '\012', '\161', '\126', + '\156', '\040', '\141', '\156', '\040', '\061', '\012', '\121', + '\156', '\161', '\040', '\141', '\156', '\040', '\061', '\012', + '\147', '\104', '\142', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\126', '\162', '\040', '\164', '\150', '\040', + '\061', '\012', '\172', '\113', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\106', '\170', '\171', '\040', '\156', + '\171', '\040', '\061', '\012', '\157', '\132', '\152', '\040', + '\157', '\156', '\040', '\061', '\012', '\172', '\101', '\171', + '\040', '\163', '\172', '\040', '\061', '\012', '\152', '\115', + '\155', '\040', '\151', '\152', '\040', '\061', '\012', '\155', + '\166', '\111', '\040', '\166', '\141', '\040', '\061', '\012', + '\106', '\167', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\172', '\161', '\154', '\040', '\161', '\165', '\040', + '\061', '\012', '\145', '\126', '\166', '\040', '\145', '\162', + '\040', '\061', '\012', '\171', '\127', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\114', '\167', '\153', '\040', + '\153', '\141', '\040', '\061', '\012', '\114', '\155', '\167', + '\040', '\155', '\145', '\040', '\061', '\012', '\166', '\130', + '\142', '\040', '\166', '\141', '\040', '\061', '\012', '\130', + '\150', '\163', '\040', '\164', '\150', '\040', '\061', '\012', + '\150', '\154', '\122', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\161', '\167', '\040', '\161', '\165', '\040', + '\061', '\012', '\172', '\142', '\113', '\040', '\163', '\172', + '\040', '\061', '\012', '\120', '\170', '\154', '\040', '\154', + '\145', '\040', '\061', '\012', '\156', '\120', '\155', '\040', + '\141', '\156', '\040', '\061', '\012', '\167', '\121', '\157', + '\040', '\157', '\156', '\040', '\061', '\012', '\104', '\143', + '\142', '\040', '\143', '\150', '\040', '\061', '\012', '\150', + '\152', '\124', '\040', '\164', '\150', '\040', '\061', '\012', + '\162', '\152', '\112', '\040', '\145', '\162', '\040', '\061', + '\012', '\142', '\115', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\151', '\131', '\142', '\040', '\151', '\156', + '\040', '\061', '\012', '\106', '\161', '\152', '\040', '\161', + '\165', '\040', '\061', '\012', '\125', '\157', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\130', '\166', '\160', + '\040', '\166', '\141', '\040', '\061', '\012', '\114', '\167', + '\142', '\040', '\167', '\141', '\040', '\061', '\012', '\112', + '\160', '\144', '\040', '\144', '\145', '\040', '\061', '\012', + '\161', '\125', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\112', '\170', '\040', '\154', '\145', '\040', + '\061', '\012', '\130', '\167', '\144', '\040', '\144', '\145', + '\040', '\061', '\012', '\170', '\113', '\146', '\040', '\146', + '\157', '\040', '\061', '\012', '\132', '\156', '\161', '\040', + '\141', '\156', '\040', '\061', '\012', '\161', '\103', '\142', + '\040', '\161', '\165', '\040', '\061', '\012', '\132', '\142', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\121', + '\165', '\170', '\040', '\161', '\165', '\040', '\061', '\012', + '\161', '\116', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\166', '\126', '\040', '\166', '\141', '\040', + '\061', '\012', '\121', '\161', '\172', '\040', '\161', '\165', + '\040', '\061', '\012', '\110', '\144', '\146', '\040', '\144', + '\145', '\040', '\061', '\012', '\171', '\123', '\170', '\040', + '\156', '\171', '\040', '\061', '\012', '\161', '\123', '\155', + '\040', '\161', '\165', '\040', '\061', '\012', '\114', '\150', + '\142', '\040', '\164', '\150', '\040', '\061', '\012', '\115', + '\166', '\146', '\040', '\166', '\141', '\040', '\061', '\012', + '\143', '\104', '\160', '\040', '\143', '\150', '\040', '\061', + '\012', '\142', '\110', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\127', '\155', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\171', '\164', '\107', '\040', '\164', + '\150', '\040', '\061', '\012', '\144', '\142', '\112', '\040', + '\144', '\145', '\040', '\061', '\012', '\106', '\146', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\150', '\166', + '\115', '\040', '\164', '\150', '\040', '\061', '\012', '\127', + '\161', '\171', '\040', '\161', '\165', '\040', '\061', '\012', + '\147', '\130', '\144', '\040', '\156', '\147', '\040', '\061', + '\012', '\165', '\106', '\147', '\040', '\161', '\165', '\040', + '\061', '\012', '\152', '\160', '\122', '\040', '\151', '\152', + '\040', '\061', '\012', '\130', '\143', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\124', '\142', '\160', '\040', + '\160', '\162', '\040', '\061', '\012', '\121', '\167', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\164', '\120', + '\160', '\040', '\164', '\150', '\040', '\061', '\012', '\146', + '\115', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\161', '\151', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\144', '\143', '\102', '\040', '\143', '\150', '\040', + '\061', '\012', '\144', '\106', '\170', '\040', '\144', '\145', + '\040', '\061', '\012', '\131', '\155', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\114', '\144', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\154', '\170', '\126', + '\040', '\154', '\145', '\040', '\061', '\012', '\143', '\103', + '\153', '\040', '\143', '\150', '\040', '\061', '\012', '\150', + '\126', '\170', '\040', '\164', '\150', '\040', '\061', '\012', + '\144', '\154', '\124', '\040', '\154', '\145', '\040', '\061', + '\012', '\153', '\150', '\120', '\040', '\164', '\150', '\040', + '\061', '\012', '\161', '\126', '\147', '\040', '\161', '\165', + '\040', '\061', '\012', '\114', '\152', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\172', '\103', '\166', '\040', + '\163', '\172', '\040', '\061', '\012', '\171', '\167', '\126', + '\040', '\167', '\141', '\040', '\061', '\012', '\171', '\142', + '\132', '\040', '\142', '\145', '\040', '\061', '\012', '\166', + '\107', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\102', '\166', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\132', '\161', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\107', '\167', '\153', '\040', '\153', '\141', + '\040', '\061', '\012', '\161', '\114', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\146', '\153', '\130', '\040', + '\153', '\141', '\040', '\061', '\012', '\116', '\142', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\142', '\130', + '\155', '\040', '\155', '\145', '\040', '\061', '\012', '\144', + '\121', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\165', '\131', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\131', '\163', '\040', '\163', '\164', '\040', + '\061', '\012', '\172', '\123', '\163', '\040', '\163', '\164', + '\040', '\061', '\012', '\171', '\143', '\132', '\040', '\143', + '\150', '\040', '\061', '\012', '\154', '\156', '\125', '\040', + '\141', '\156', '\040', '\061', '\012', '\164', '\103', '\152', + '\040', '\164', '\150', '\040', '\061', '\012', '\170', '\156', + '\131', '\040', '\141', '\156', '\040', '\061', '\012', '\160', + '\164', '\121', '\040', '\164', '\150', '\040', '\061', '\012', + '\163', '\167', '\117', '\040', '\163', '\164', '\040', '\061', + '\012', '\150', '\130', '\165', '\040', '\164', '\150', '\040', + '\061', '\012', '\155', '\102', '\167', '\040', '\155', '\142', + '\040', '\061', '\012', '\167', '\155', '\106', '\040', '\155', + '\145', '\040', '\061', '\012', '\170', '\112', '\170', '\040', + '\170', '\145', '\040', '\061', '\012', '\144', '\130', '\152', + '\040', '\144', '\145', '\040', '\061', '\012', '\145', '\161', + '\147', '\040', '\161', '\165', '\040', '\061', '\012', '\156', + '\102', '\146', '\040', '\141', '\156', '\040', '\061', '\012', + '\130', '\142', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\143', '\121', '\040', '\143', '\150', '\040', + '\061', '\012', '\170', '\153', '\123', '\040', '\153', '\141', + '\040', '\061', '\012', '\164', '\117', '\161', '\040', '\164', + '\150', '\040', '\061', '\012', '\165', '\121', '\142', '\040', + '\161', '\165', '\040', '\061', '\012', '\143', '\166', '\126', + '\040', '\143', '\150', '\040', '\061', '\012', '\163', '\102', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\144', + '\103', '\153', '\040', '\144', '\145', '\040', '\061', '\012', + '\143', '\113', '\166', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\126', '\146', '\040', '\143', '\150', '\040', + '\061', '\012', '\167', '\132', '\170', '\040', '\167', '\141', + '\040', '\061', '\012', '\102', '\166', '\155', '\040', '\166', + '\141', '\040', '\061', '\012', '\154', '\161', '\112', '\040', + '\161', '\165', '\040', '\061', '\012', '\146', '\170', '\122', + '\040', '\146', '\157', '\040', '\061', '\012', '\166', '\155', + '\106', '\040', '\166', '\141', '\040', '\061', '\012', '\170', + '\156', '\161', '\040', '\141', '\156', '\040', '\061', '\012', + '\142', '\102', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\164', '\120', '\144', '\040', '\164', '\150', '\040', + '\061', '\012', '\146', '\116', '\163', '\040', '\163', '\164', + '\040', '\061', '\012', '\106', '\153', '\160', '\040', '\153', + '\141', '\040', '\061', '\012', '\131', '\171', '\145', '\040', + '\145', '\162', '\040', '\061', '\012', '\125', '\142', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\170', '\172', + '\120', '\040', '\163', '\172', '\040', '\061', '\012', '\146', + '\155', '\121', '\040', '\155', '\145', '\040', '\061', '\012', + '\161', '\143', '\101', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\113', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\170', '\166', '\132', '\040', '\166', '\141', + '\040', '\061', '\012', '\143', '\142', '\116', '\040', '\143', + '\150', '\040', '\061', '\012', '\171', '\131', '\154', '\040', + '\154', '\145', '\040', '\061', '\012', '\120', '\155', '\167', + '\040', '\155', '\145', '\040', '\061', '\012', '\167', '\106', + '\170', '\040', '\167', '\141', '\040', '\061', '\012', '\150', + '\122', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\161', '\160', '\123', '\040', '\161', '\165', '\040', '\061', + '\012', '\126', '\161', '\146', '\040', '\161', '\165', '\040', + '\061', '\012', '\107', '\150', '\147', '\040', '\164', '\150', + '\040', '\061', '\012', '\127', '\166', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\170', '\153', '\103', '\040', + '\153', '\141', '\040', '\061', '\012', '\171', '\164', '\115', + '\040', '\164', '\150', '\040', '\061', '\012', '\114', '\156', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\144', + '\170', '\104', '\040', '\144', '\145', '\040', '\061', '\012', + '\142', '\115', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\170', '\166', '\125', '\040', '\166', '\141', '\040', + '\061', '\012', '\121', '\172', '\170', '\040', '\163', '\172', + '\040', '\061', '\012', '\163', '\162', '\115', '\040', '\145', + '\162', '\040', '\061', '\012', '\166', '\114', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\143', '\107', '\161', + '\040', '\143', '\150', '\040', '\061', '\012', '\126', '\155', + '\171', '\040', '\155', '\145', '\040', '\061', '\012', '\150', + '\143', '\114', '\040', '\164', '\150', '\040', '\061', '\012', + '\160', '\113', '\170', '\040', '\160', '\162', '\040', '\061', + '\012', '\112', '\170', '\163', '\040', '\163', '\164', '\040', + '\061', '\012', '\142', '\154', '\127', '\040', '\154', '\145', + '\040', '\061', '\012', '\160', '\121', '\157', '\040', '\157', + '\156', '\040', '\061', '\012', '\142', '\105', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\146', '\127', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\163', '\131', + '\155', '\040', '\163', '\164', '\040', '\061', '\012', '\156', + '\113', '\167', '\040', '\141', '\156', '\040', '\061', '\012', + '\144', '\164', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\124', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\145', '\160', '\130', '\040', '\145', '\162', + '\040', '\061', '\012', '\146', '\103', '\160', '\040', '\160', + '\162', '\040', '\061', '\012', '\142', '\106', '\153', '\040', + '\153', '\141', '\040', '\061', '\012', '\122', '\172', '\142', + '\040', '\163', '\172', '\040', '\061', '\012', '\166', '\161', + '\111', '\040', '\161', '\165', '\040', '\061', '\012', '\132', + '\150', '\143', '\040', '\164', '\150', '\040', '\061', '\012', + '\110', '\166', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\155', '\126', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\111', '\167', '\170', '\040', '\167', '\141', + '\040', '\061', '\012', '\160', '\150', '\122', '\040', '\164', + '\150', '\040', '\061', '\012', '\167', '\116', '\142', '\040', + '\167', '\141', '\040', '\061', '\012', '\146', '\122', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\154', '\152', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\154', + '\166', '\131', '\040', '\154', '\145', '\040', '\061', '\012', + '\152', '\143', '\101', '\040', '\143', '\150', '\040', '\061', + '\012', '\144', '\107', '\167', '\040', '\144', '\145', '\040', + '\061', '\012', '\103', '\161', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\155', '\102', '\170', '\040', '\155', + '\145', '\040', '\061', '\012', '\115', '\155', '\170', '\040', + '\155', '\145', '\040', '\061', '\012', '\126', '\170', '\141', + '\040', '\141', '\156', '\040', '\061', '\012', '\130', '\150', + '\167', '\040', '\164', '\150', '\040', '\061', '\012', '\145', + '\161', '\113', '\040', '\161', '\165', '\040', '\061', '\012', + '\164', '\103', '\167', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\166', '\125', '\040', '\163', '\172', '\040', + '\061', '\012', '\154', '\170', '\121', '\040', '\154', '\145', + '\040', '\061', '\012', '\166', '\115', '\166', '\040', '\166', + '\141', '\040', '\061', '\012', '\147', '\161', '\101', '\040', + '\161', '\165', '\040', '\061', '\012', '\112', '\142', '\156', + '\040', '\141', '\156', '\040', '\061', '\012', '\147', '\103', + '\152', '\040', '\156', '\147', '\040', '\061', '\012', '\157', + '\124', '\146', '\040', '\157', '\156', '\040', '\061', '\012', + '\153', '\142', '\127', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\152', '\131', '\040', '\161', '\165', '\040', + '\061', '\012', '\122', '\161', '\146', '\040', '\161', '\165', + '\040', '\061', '\012', '\150', '\131', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\171', '\150', '\105', '\040', + '\164', '\150', '\040', '\061', '\012', '\147', '\131', '\152', + '\040', '\156', '\147', '\040', '\061', '\012', '\152', '\143', + '\111', '\040', '\143', '\150', '\040', '\061', '\012', '\161', + '\166', '\112', '\040', '\161', '\165', '\040', '\061', '\012', + '\161', '\157', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\106', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\161', '\161', '\110', '\040', '\161', '\165', + '\040', '\061', '\012', '\116', '\170', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\167', '\126', '\157', '\040', + '\157', '\156', '\040', '\061', '\012', '\172', '\110', '\166', + '\040', '\163', '\172', '\040', '\061', '\012', '\171', '\142', + '\123', '\040', '\142', '\145', '\040', '\061', '\012', '\110', + '\167', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\115', '\170', '\141', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\153', '\114', '\040', '\153', '\141', '\040', + '\061', '\012', '\161', '\155', '\117', '\040', '\161', '\165', + '\040', '\061', '\012', '\161', '\142', '\122', '\040', '\161', + '\165', '\040', '\061', '\012', '\132', '\146', '\171', '\040', + '\156', '\171', '\040', '\061', '\012', '\122', '\153', '\146', + '\040', '\153', '\141', '\040', '\061', '\012', '\166', '\147', + '\126', '\040', '\156', '\147', '\040', '\061', '\012', '\150', + '\102', '\167', '\040', '\164', '\150', '\040', '\061', '\012', + '\160', '\130', '\170', '\040', '\160', '\162', '\040', '\061', + '\012', '\142', '\162', '\121', '\040', '\145', '\162', '\040', + '\061', '\012', '\146', '\166', '\117', '\040', '\166', '\141', + '\040', '\061', '\012', '\150', '\104', '\143', '\040', '\164', + '\150', '\040', '\061', '\012', '\170', '\121', '\141', '\040', + '\141', '\156', '\040', '\061', '\012', '\167', '\146', '\106', + '\040', '\167', '\141', '\040', '\061', '\012', '\150', '\132', + '\170', '\040', '\164', '\150', '\040', '\061', '\012', '\112', + '\147', '\172', '\040', '\156', '\147', '\040', '\061', '\012', + '\161', '\156', '\131', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\130', '\154', '\040', '\154', '\145', '\040', + '\061', '\012', '\145', '\116', '\142', '\040', '\145', '\162', + '\040', '\061', '\012', '\146', '\170', '\123', '\040', '\146', + '\157', '\040', '\061', '\012', '\163', '\116', '\153', '\040', + '\163', '\164', '\040', '\061', '\012', '\155', '\106', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\125', '\165', + '\170', '\040', '\161', '\165', '\040', '\061', '\012', '\131', + '\144', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\157', '\172', '\127', '\040', '\157', '\156', '\040', '\061', + '\012', '\130', '\172', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\112', '\146', '\145', '\040', '\145', '\162', + '\040', '\061', '\012', '\106', '\164', '\170', '\040', '\164', + '\150', '\040', '\061', '\012', '\166', '\172', '\122', '\040', + '\163', '\172', '\040', '\061', '\012', '\167', '\132', '\153', + '\040', '\153', '\141', '\040', '\061', '\012', '\157', '\110', + '\172', '\040', '\157', '\156', '\040', '\061', '\012', '\161', + '\166', '\124', '\040', '\161', '\165', '\040', '\061', '\012', + '\161', '\157', '\101', '\040', '\161', '\165', '\040', '\061', + '\012', '\123', '\144', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\164', '\170', '\127', '\040', '\164', '\150', + '\040', '\061', '\012', '\105', '\147', '\146', '\040', '\156', + '\147', '\040', '\061', '\012', '\144', '\115', '\146', '\040', + '\144', '\145', '\040', '\061', '\012', '\122', '\150', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\166', '\122', + '\156', '\040', '\141', '\156', '\040', '\061', '\012', '\165', + '\152', '\130', '\040', '\161', '\165', '\040', '\061', '\012', + '\146', '\122', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\147', '\152', '\101', '\040', '\156', '\147', '\040', + '\061', '\012', '\147', '\104', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\163', '\155', '\132', '\040', '\163', + '\164', '\040', '\061', '\012', '\152', '\111', '\144', '\040', + '\144', '\145', '\040', '\061', '\012', '\161', '\153', '\115', + '\040', '\161', '\165', '\040', '\061', '\012', '\142', '\113', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\163', + '\103', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\165', '\124', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\126', '\163', '\040', '\154', '\145', '\040', + '\061', '\012', '\165', '\121', '\157', '\040', '\161', '\165', + '\040', '\061', '\012', '\112', '\146', '\163', '\040', '\163', + '\164', '\040', '\061', '\012', '\166', '\113', '\155', '\040', + '\166', '\141', '\040', '\061', '\012', '\152', '\121', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\146', '\125', + '\146', '\040', '\146', '\157', '\040', '\061', '\012', '\165', + '\124', '\146', '\040', '\161', '\165', '\040', '\061', '\012', + '\102', '\156', '\166', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\144', '\125', '\040', '\164', '\150', '\040', + '\061', '\012', '\144', '\170', '\131', '\040', '\144', '\145', + '\040', '\061', '\012', '\150', '\147', '\126', '\040', '\164', + '\150', '\040', '\061', '\012', '\132', '\144', '\146', '\040', + '\144', '\145', '\040', '\061', '\012', '\150', '\161', '\123', + '\040', '\164', '\150', '\040', '\061', '\012', '\145', '\112', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\161', + '\107', '\165', '\040', '\165', '\156', '\040', '\061', '\012', + '\166', '\155', '\105', '\040', '\166', '\141', '\040', '\061', + '\012', '\147', '\113', '\172', '\040', '\156', '\147', '\040', + '\061', '\012', '\155', '\125', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\126', '\152', '\171', '\040', '\151', + '\152', '\040', '\061', '\012', '\165', '\166', '\112', '\040', + '\161', '\165', '\040', '\061', '\012', '\155', '\110', '\162', + '\040', '\145', '\162', '\040', '\061', '\012', '\115', '\150', + '\166', '\040', '\164', '\150', '\040', '\061', '\012', '\172', + '\163', '\132', '\040', '\163', '\164', '\040', '\061', '\012', + '\126', '\172', '\171', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\113', '\142', '\040', '\151', '\152', '\040', + '\061', '\012', '\172', '\120', '\160', '\040', '\163', '\172', + '\040', '\061', '\012', '\161', '\147', '\104', '\040', '\161', + '\165', '\040', '\061', '\012', '\130', '\150', '\146', '\040', + '\164', '\150', '\040', '\061', '\012', '\117', '\147', '\160', + '\040', '\156', '\147', '\040', '\061', '\012', '\152', '\167', + '\130', '\040', '\151', '\152', '\040', '\061', '\012', '\154', + '\131', '\171', '\040', '\154', '\145', '\040', '\061', '\012', + '\161', '\172', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\130', '\152', '\040', '\152', '\157', '\040', + '\061', '\012', '\113', '\160', '\170', '\040', '\160', '\162', + '\040', '\061', '\012', '\171', '\144', '\131', '\040', '\144', + '\145', '\040', '\061', '\012', '\166', '\102', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\132', '\160', '\160', + '\040', '\160', '\162', '\040', '\061', '\012', '\142', '\104', + '\144', '\040', '\144', '\145', '\040', '\061', '\012', '\106', + '\152', '\153', '\040', '\151', '\152', '\040', '\061', '\012', + '\153', '\144', '\101', '\040', '\144', '\145', '\040', '\061', + '\012', '\172', '\127', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\167', '\123', '\144', '\040', '\144', '\145', + '\040', '\061', '\012', '\153', '\106', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\123', '\170', '\154', '\040', + '\154', '\145', '\040', '\061', '\012', '\106', '\166', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\160', '\142', + '\122', '\040', '\160', '\162', '\040', '\061', '\012', '\161', + '\162', '\104', '\040', '\161', '\165', '\040', '\061', '\012', + '\166', '\132', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\166', '\125', '\155', '\040', '\166', '\141', '\040', + '\061', '\012', '\167', '\105', '\171', '\040', '\167', '\141', + '\040', '\061', '\012', '\152', '\152', '\110', '\040', '\152', + '\157', '\040', '\061', '\012', '\163', '\104', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\125', '\152', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\153', '\156', + '\111', '\040', '\141', '\156', '\040', '\061', '\012', '\146', + '\117', '\141', '\040', '\141', '\156', '\040', '\061', '\012', + '\103', '\152', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\164', '\142', '\126', '\040', '\164', '\150', '\040', + '\061', '\012', '\147', '\161', '\144', '\040', '\161', '\165', + '\040', '\061', '\012', '\145', '\120', '\170', '\040', '\145', + '\162', '\040', '\061', '\012', '\167', '\122', '\155', '\040', + '\155', '\145', '\040', '\061', '\012', '\160', '\166', '\107', + '\040', '\166', '\141', '\040', '\061', '\012', '\121', '\171', + '\154', '\040', '\154', '\145', '\040', '\061', '\012', '\143', + '\167', '\107', '\040', '\143', '\150', '\040', '\061', '\012', + '\104', '\164', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\120', '\142', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\122', '\147', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\146', '\152', '\125', '\040', '\151', + '\152', '\040', '\061', '\012', '\152', '\112', '\146', '\040', + '\151', '\152', '\040', '\061', '\012', '\122', '\170', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\112', '\164', + '\170', '\040', '\164', '\150', '\040', '\061', '\012', '\161', + '\166', '\132', '\040', '\161', '\165', '\040', '\061', '\012', + '\153', '\113', '\155', '\040', '\153', '\141', '\040', '\061', + '\012', '\150', '\106', '\155', '\040', '\164', '\150', '\040', + '\061', '\012', '\153', '\143', '\130', '\040', '\143', '\150', + '\040', '\061', '\012', '\146', '\116', '\155', '\040', '\155', + '\145', '\040', '\061', '\012', '\142', '\160', '\102', '\040', + '\160', '\162', '\040', '\061', '\012', '\170', '\161', '\131', + '\040', '\161', '\165', '\040', '\061', '\012', '\150', '\131', + '\171', '\040', '\164', '\150', '\040', '\061', '\012', '\147', + '\107', '\160', '\040', '\156', '\147', '\040', '\061', '\012', + '\126', '\146', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\167', '\104', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\142', '\124', '\163', '\040', '\163', '\164', + '\040', '\061', '\012', '\150', '\146', '\126', '\040', '\164', + '\150', '\040', '\061', '\012', '\161', '\172', '\160', '\040', + '\161', '\165', '\040', '\061', '\012', '\171', '\125', '\166', + '\040', '\166', '\141', '\040', '\061', '\012', '\161', '\107', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\126', + '\144', '\154', '\040', '\154', '\145', '\040', '\061', '\012', + '\130', '\152', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\115', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\150', '\124', '\147', '\040', '\164', '\150', + '\040', '\061', '\012', '\110', '\154', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\164', '\113', '\172', '\040', + '\164', '\150', '\040', '\061', '\012', '\127', '\166', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\154', '\115', + '\172', '\040', '\154', '\145', '\040', '\061', '\012', '\115', + '\167', '\170', '\040', '\167', '\141', '\040', '\061', '\012', + '\127', '\154', '\166', '\040', '\154', '\145', '\040', '\061', + '\012', '\170', '\172', '\107', '\040', '\163', '\172', '\040', + '\061', '\012', '\147', '\155', '\104', '\040', '\156', '\147', + '\040', '\061', '\012', '\172', '\117', '\151', '\040', '\151', + '\156', '\040', '\061', '\012', '\142', '\142', '\111', '\040', + '\142', '\145', '\040', '\061', '\012', '\142', '\160', '\111', + '\040', '\160', '\162', '\040', '\061', '\012', '\146', '\121', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\160', + '\121', '\166', '\040', '\166', '\141', '\040', '\061', '\012', + '\166', '\105', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\152', '\106', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\127', '\150', '\146', '\040', '\164', '\150', + '\040', '\061', '\012', '\152', '\166', '\121', '\040', '\151', + '\152', '\040', '\061', '\012', '\161', '\131', '\170', '\040', + '\161', '\165', '\040', '\061', '\012', '\162', '\170', '\115', + '\040', '\145', '\162', '\040', '\061', '\012', '\166', '\120', + '\160', '\040', '\166', '\141', '\040', '\061', '\012', '\146', + '\152', '\104', '\040', '\151', '\152', '\040', '\061', '\012', + '\126', '\167', '\171', '\040', '\167', '\141', '\040', '\061', + '\012', '\131', '\161', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\164', '\143', '\127', '\040', '\164', '\150', + '\040', '\061', '\012', '\152', '\131', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\147', '\112', '\142', '\040', + '\156', '\147', '\040', '\061', '\012', '\124', '\153', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\161', '\150', + '\152', '\040', '\164', '\150', '\040', '\061', '\012', '\152', + '\170', '\106', '\040', '\151', '\152', '\040', '\061', '\012', + '\106', '\160', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\153', '\130', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\154', '\147', '\132', '\040', '\156', '\147', + '\040', '\061', '\012', '\172', '\156', '\111', '\040', '\141', + '\156', '\040', '\061', '\012', '\161', '\171', '\116', '\040', + '\161', '\165', '\040', '\061', '\012', '\166', '\102', '\152', + '\040', '\151', '\152', '\040', '\061', '\012', '\152', '\123', + '\170', '\040', '\151', '\152', '\040', '\061', '\012', '\143', + '\161', '\111', '\040', '\143', '\150', '\040', '\061', '\012', + '\161', '\131', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\132', '\162', '\162', '\040', '\145', '\162', '\040', + '\061', '\012', '\163', '\110', '\162', '\040', '\145', '\162', + '\040', '\061', '\012', '\166', '\162', '\113', '\040', '\145', + '\162', '\040', '\061', '\012', '\160', '\142', '\110', '\040', + '\160', '\162', '\040', '\061', '\012', '\172', '\126', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\144', '\121', + '\142', '\040', '\144', '\145', '\040', '\061', '\012', '\154', + '\170', '\106', '\040', '\154', '\145', '\040', '\061', '\012', + '\163', '\147', '\127', '\040', '\156', '\147', '\040', '\061', + '\012', '\107', '\150', '\146', '\040', '\164', '\150', '\040', + '\061', '\012', '\170', '\160', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\161', '\150', '\116', '\040', '\164', + '\150', '\040', '\061', '\012', '\106', '\163', '\146', '\040', + '\163', '\164', '\040', '\061', '\012', '\121', '\147', '\141', + '\040', '\141', '\156', '\040', '\061', '\012', '\122', '\144', + '\160', '\040', '\144', '\145', '\040', '\061', '\012', '\146', + '\166', '\113', '\040', '\166', '\141', '\040', '\061', '\012', + '\131', '\144', '\172', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\166', '\127', '\040', '\166', '\141', '\040', + '\061', '\012', '\143', '\120', '\155', '\040', '\143', '\150', + '\040', '\061', '\012', '\143', '\121', '\171', '\040', '\143', + '\150', '\040', '\061', '\012', '\171', '\167', '\106', '\040', + '\167', '\141', '\040', '\061', '\012', '\131', '\160', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\122', '\163', + '\152', '\040', '\163', '\164', '\040', '\061', '\012', '\131', + '\147', '\167', '\040', '\156', '\147', '\040', '\061', '\012', + '\170', '\126', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\171', '\170', '\114', '\040', '\156', '\171', '\040', + '\061', '\012', '\131', '\167', '\154', '\040', '\154', '\145', + '\040', '\061', '\012', '\152', '\115', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\172', '\124', '\154', '\040', + '\154', '\145', '\040', '\061', '\012', '\141', '\111', '\161', + '\040', '\141', '\156', '\040', '\061', '\012', '\161', '\121', + '\151', '\040', '\161', '\165', '\040', '\061', '\012', '\164', + '\161', '\111', '\040', '\164', '\150', '\040', '\061', '\012', + '\110', '\166', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\121', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\150', '\146', '\107', '\040', '\164', '\150', + '\040', '\061', '\012', '\143', '\124', '\144', '\040', '\143', + '\150', '\040', '\061', '\012', '\142', '\146', '\121', '\040', + '\142', '\145', '\040', '\061', '\012', '\113', '\146', '\144', + '\040', '\144', '\145', '\040', '\061', '\012', '\143', '\130', + '\163', '\040', '\143', '\150', '\040', '\061', '\012', '\166', + '\131', '\170', '\040', '\166', '\141', '\040', '\061', '\012', + '\121', '\157', '\143', '\040', '\162', '\157', '\040', '\061', + '\012', '\166', '\162', '\114', '\040', '\145', '\162', '\040', + '\061', '\012', '\160', '\132', '\153', '\040', '\153', '\141', + '\040', '\061', '\012', '\143', '\144', '\130', '\040', '\143', + '\150', '\040', '\061', '\012', '\131', '\147', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\154', '\156', '\117', + '\040', '\141', '\156', '\040', '\061', '\012', '\155', '\146', + '\131', '\040', '\155', '\145', '\040', '\061', '\012', '\146', + '\156', '\126', '\040', '\141', '\156', '\040', '\061', '\012', + '\155', '\142', '\132', '\040', '\155', '\145', '\040', '\061', + '\012', '\147', '\142', '\105', '\040', '\156', '\147', '\040', + '\061', '\012', '\170', '\152', '\132', '\040', '\151', '\152', + '\040', '\061', '\012', '\106', '\160', '\171', '\040', '\160', + '\162', '\040', '\061', '\012', '\156', '\160', '\105', '\040', + '\141', '\156', '\040', '\061', '\012', '\122', '\170', '\171', + '\040', '\156', '\171', '\040', '\061', '\012', '\157', '\127', + '\160', '\040', '\157', '\156', '\040', '\061', '\012', '\150', + '\126', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\171', '\112', '\146', '\040', '\156', '\171', '\040', '\061', + '\012', '\163', '\121', '\144', '\040', '\163', '\164', '\040', + '\061', '\012', '\132', '\166', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\142', '\104', '\155', '\040', '\155', + '\145', '\040', '\061', '\012', '\160', '\114', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\167', '\167', '\106', + '\040', '\167', '\141', '\040', '\061', '\012', '\170', '\102', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\161', + '\113', '\155', '\040', '\161', '\165', '\040', '\061', '\012', + '\167', '\130', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\111', '\165', '\170', '\040', '\161', '\165', '\040', + '\061', '\012', '\144', '\147', '\102', '\040', '\156', '\147', + '\040', '\061', '\012', '\147', '\112', '\160', '\040', '\156', + '\147', '\040', '\061', '\012', '\161', '\147', '\170', '\040', + '\161', '\165', '\040', '\061', '\012', '\146', '\116', '\150', + '\040', '\150', '\157', '\040', '\061', '\012', '\143', '\166', + '\105', '\040', '\143', '\150', '\040', '\061', '\012', '\143', + '\147', '\110', '\040', '\143', '\150', '\040', '\061', '\012', + '\154', '\116', '\163', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\104', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\172', '\143', '\107', '\040', '\143', '\150', + '\040', '\061', '\012', '\146', '\132', '\156', '\040', '\157', + '\156', '\040', '\061', '\012', '\165', '\125', '\170', '\040', + '\161', '\165', '\040', '\061', '\012', '\143', '\154', '\121', + '\040', '\154', '\145', '\040', '\061', '\012', '\146', '\144', + '\110', '\040', '\144', '\145', '\040', '\061', '\012', '\145', + '\132', '\152', '\040', '\145', '\162', '\040', '\061', '\012', + '\126', '\161', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\122', '\143', '\170', '\040', '\143', '\150', '\040', + '\061', '\012', '\152', '\107', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\161', '\172', '\115', '\040', '\163', + '\172', '\040', '\061', '\012', '\121', '\160', '\167', '\040', + '\160', '\162', '\040', '\061', '\012', '\123', '\160', '\170', + '\040', '\160', '\162', '\040', '\061', '\012', '\143', '\107', + '\170', '\040', '\143', '\150', '\040', '\061', '\012', '\143', + '\161', '\101', '\040', '\143', '\150', '\040', '\061', '\012', + '\166', '\142', '\113', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\145', '\127', '\040', '\145', '\162', '\040', + '\061', '\012', '\166', '\153', '\103', '\040', '\153', '\141', + '\040', '\061', '\012', '\170', '\172', '\102', '\040', '\163', + '\172', '\040', '\061', '\012', '\170', '\165', '\122', '\040', + '\161', '\165', '\040', '\061', '\012', '\117', '\171', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\115', '\161', + '\170', '\040', '\161', '\165', '\040', '\061', '\012', '\161', + '\161', '\152', '\040', '\161', '\165', '\040', '\061', '\012', + '\171', '\161', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\167', '\114', '\040', '\143', '\150', '\040', + '\061', '\012', '\160', '\120', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\144', '\123', '\170', '\040', '\144', + '\145', '\040', '\061', '\012', '\144', '\120', '\153', '\040', + '\144', '\145', '\040', '\061', '\012', '\165', '\172', '\110', + '\040', '\161', '\165', '\040', '\061', '\012', '\146', '\166', + '\110', '\040', '\166', '\141', '\040', '\061', '\012', '\160', + '\143', '\110', '\040', '\143', '\150', '\040', '\061', '\012', + '\150', '\154', '\131', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\164', '\130', '\040', '\164', '\150', '\040', + '\061', '\012', '\116', '\166', '\163', '\040', '\163', '\164', + '\040', '\061', '\012', '\150', '\166', '\114', '\040', '\164', + '\150', '\040', '\061', '\012', '\172', '\122', '\153', '\040', + '\163', '\172', '\040', '\061', '\012', '\164', '\116', '\152', + '\040', '\164', '\150', '\040', '\061', '\012', '\104', '\142', + '\166', '\040', '\166', '\141', '\040', '\061', '\012', '\152', + '\113', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\144', '\113', '\171', '\040', '\144', '\145', '\040', '\061', + '\012', '\171', '\126', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\151', '\161', '\112', '\040', '\161', '\165', + '\040', '\061', '\012', '\172', '\147', '\112', '\040', '\156', + '\147', '\040', '\061', '\012', '\145', '\112', '\163', '\040', + '\145', '\162', '\040', '\061', '\012', '\167', '\117', '\170', + '\040', '\167', '\141', '\040', '\061', '\012', '\162', '\130', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\110', + '\161', '\160', '\040', '\161', '\165', '\040', '\061', '\012', + '\166', '\127', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\142', '\124', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\146', '\103', '\171', '\040', '\156', '\171', + '\040', '\061', '\012', '\141', '\117', '\161', '\040', '\141', + '\156', '\040', '\061', '\012', '\157', '\103', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\160', '\156', '\105', + '\040', '\141', '\156', '\040', '\061', '\012', '\106', '\167', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\172', + '\162', '\124', '\040', '\145', '\162', '\040', '\061', '\012', + '\170', '\110', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\171', '\144', '\130', '\040', '\144', '\145', '\040', + '\061', '\012', '\144', '\153', '\126', '\040', '\144', '\145', + '\040', '\061', '\012', '\122', '\161', '\171', '\040', '\161', + '\165', '\040', '\061', '\012', '\132', '\171', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\153', '\130', '\154', + '\040', '\154', '\145', '\040', '\061', '\012', '\157', '\112', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\163', + '\170', '\111', '\040', '\163', '\164', '\040', '\061', '\012', + '\161', '\132', '\167', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\161', '\170', '\040', '\161', '\165', '\040', + '\061', '\012', '\143', '\154', '\132', '\040', '\143', '\150', + '\040', '\061', '\012', '\163', '\167', '\130', '\040', '\163', + '\172', '\040', '\061', '\012', '\141', '\110', '\167', '\040', + '\141', '\156', '\040', '\061', '\012', '\162', '\127', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\143', '\121', + '\160', '\040', '\143', '\150', '\040', '\061', '\012', '\112', + '\167', '\152', '\040', '\151', '\152', '\040', '\061', '\012', + '\161', '\145', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\121', '\152', '\040', '\163', '\164', '\040', + '\061', '\012', '\122', '\160', '\142', '\040', '\160', '\162', + '\040', '\061', '\012', '\155', '\132', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\162', '\102', '\170', '\040', + '\145', '\162', '\040', '\061', '\012', '\155', '\170', '\126', + '\040', '\155', '\145', '\040', '\061', '\012', '\115', '\166', + '\171', '\040', '\156', '\171', '\040', '\061', '\012', '\143', + '\122', '\154', '\040', '\143', '\150', '\040', '\061', '\012', + '\106', '\172', '\166', '\040', '\163', '\172', '\040', '\061', + '\012', '\160', '\102', '\163', '\040', '\163', '\172', '\040', + '\061', '\012', '\152', '\127', '\163', '\040', '\163', '\164', + '\040', '\061', '\012', '\166', '\161', '\113', '\040', '\161', + '\165', '\040', '\061', '\012', '\111', '\170', '\154', '\040', + '\154', '\145', '\040', '\061', '\012', '\171', '\150', '\167', + '\040', '\164', '\150', '\040', '\061', '\012', '\167', '\171', + '\121', '\040', '\167', '\141', '\040', '\061', '\012', '\165', + '\103', '\142', '\040', '\161', '\165', '\040', '\061', '\012', + '\172', '\162', '\106', '\040', '\163', '\172', '\040', '\061', + '\012', '\151', '\171', '\121', '\040', '\151', '\156', '\040', + '\061', '\012', '\161', '\163', '\120', '\040', '\161', '\165', + '\040', '\061', '\012', '\150', '\114', '\162', '\040', '\145', + '\162', '\040', '\061', '\012', '\143', '\166', '\130', '\040', + '\143', '\150', '\040', '\061', '\012', '\123', '\143', '\161', + '\040', '\143', '\150', '\040', '\061', '\012', '\172', '\162', + '\114', '\040', '\145', '\162', '\040', '\061', '\012', '\145', + '\143', '\125', '\040', '\143', '\150', '\040', '\061', '\012', + '\126', '\170', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\146', '\103', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\157', '\166', '\130', '\040', '\157', '\156', + '\040', '\061', '\012', '\125', '\161', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\163', '\126', '\167', '\040', + '\163', '\164', '\040', '\061', '\012', '\163', '\160', '\130', + '\040', '\163', '\164', '\040', '\061', '\012', '\121', '\153', + '\166', '\040', '\153', '\141', '\040', '\061', '\012', '\146', + '\171', '\127', '\040', '\156', '\171', '\040', '\061', '\012', + '\162', '\102', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\155', '\144', '\103', '\040', '\144', '\145', '\040', + '\061', '\012', '\127', '\152', '\153', '\040', '\151', '\152', + '\040', '\061', '\012', '\152', '\131', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\150', '\130', '\161', '\040', + '\164', '\150', '\040', '\061', '\012', '\170', '\153', '\155', + '\040', '\153', '\141', '\040', '\061', '\012', '\150', '\150', + '\125', '\040', '\164', '\150', '\040', '\061', '\012', '\104', + '\166', '\172', '\040', '\163', '\172', '\040', '\061', '\012', + '\164', '\143', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\132', '\171', '\040', '\167', '\141', '\040', + '\061', '\012', '\152', '\164', '\103', '\040', '\164', '\150', + '\040', '\061', '\012', '\161', '\156', '\104', '\040', '\141', + '\156', '\040', '\061', '\012', '\166', '\155', '\102', '\040', + '\166', '\141', '\040', '\061', '\012', '\153', '\152', '\102', + '\040', '\151', '\152', '\040', '\061', '\012', '\143', '\144', + '\107', '\040', '\143', '\150', '\040', '\061', '\012', '\126', + '\153', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\150', '\116', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\112', '\146', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\151', '\127', '\166', '\040', '\151', '\156', + '\040', '\061', '\012', '\127', '\164', '\156', '\040', '\164', + '\150', '\040', '\061', '\012', '\154', '\146', '\105', '\040', + '\154', '\145', '\040', '\061', '\012', '\144', '\132', '\142', + '\040', '\144', '\145', '\040', '\061', '\012', '\145', '\161', + '\121', '\040', '\161', '\165', '\040', '\061', '\012', '\147', + '\125', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\161', '\167', '\114', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\125', '\161', '\040', '\164', '\150', '\040', + '\061', '\012', '\150', '\107', '\143', '\040', '\164', '\150', + '\040', '\061', '\012', '\156', '\167', '\130', '\040', '\141', + '\156', '\040', '\061', '\012', '\116', '\142', '\164', '\040', + '\164', '\150', '\040', '\061', '\012', '\152', '\152', '\120', + '\040', '\151', '\152', '\040', '\061', '\012', '\163', '\161', + '\112', '\040', '\161', '\165', '\040', '\061', '\012', '\154', + '\121', '\146', '\040', '\154', '\145', '\040', '\061', '\012', + '\152', '\132', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\127', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\115', '\170', '\165', '\040', '\161', '\165', + '\040', '\061', '\012', '\161', '\106', '\151', '\040', '\161', + '\165', '\040', '\061', '\012', '\155', '\152', '\130', '\040', + '\151', '\152', '\040', '\061', '\012', '\166', '\104', '\170', + '\040', '\166', '\141', '\040', '\061', '\012', '\166', '\104', + '\156', '\040', '\141', '\156', '\040', '\061', '\012', '\167', + '\125', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\172', '\150', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\110', '\167', '\040', '\163', '\172', '\040', + '\061', '\012', '\124', '\152', '\154', '\040', '\154', '\145', + '\040', '\061', '\012', '\170', '\165', '\130', '\040', '\161', + '\165', '\040', '\061', '\012', '\152', '\132', '\160', '\040', + '\151', '\152', '\040', '\061', '\012', '\167', '\126', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\147', '\106', + '\160', '\040', '\156', '\147', '\040', '\061', '\012', '\107', + '\171', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\112', '\154', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\102', '\153', '\146', '\040', '\153', '\141', '\040', + '\061', '\012', '\150', '\150', '\112', '\040', '\164', '\150', + '\040', '\061', '\012', '\164', '\166', '\127', '\040', '\164', + '\150', '\040', '\061', '\012', '\142', '\111', '\171', '\040', + '\156', '\171', '\040', '\061', '\012', '\114', '\154', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\172', '\112', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\161', + '\145', '\121', '\040', '\161', '\165', '\040', '\061', '\012', + '\156', '\154', '\130', '\040', '\141', '\156', '\040', '\061', + '\012', '\164', '\143', '\121', '\040', '\164', '\150', '\040', + '\061', '\012', '\161', '\164', '\125', '\040', '\164', '\150', + '\040', '\061', '\012', '\146', '\153', '\127', '\040', '\153', + '\141', '\040', '\061', '\012', '\147', '\112', '\153', '\040', + '\156', '\147', '\040', '\061', '\012', '\147', '\121', '\171', + '\040', '\156', '\147', '\040', '\061', '\012', '\163', '\120', + '\172', '\040', '\163', '\164', '\040', '\061', '\012', '\142', + '\155', '\117', '\040', '\155', '\145', '\040', '\061', '\012', + '\131', '\164', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\161', '\106', '\040', '\161', '\165', '\040', + '\061', '\012', '\151', '\102', '\153', '\040', '\151', '\156', + '\040', '\061', '\012', '\165', '\172', '\126', '\040', '\161', + '\165', '\040', '\061', '\012', '\170', '\116', '\160', '\040', + '\160', '\162', '\040', '\061', '\012', '\172', '\122', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\161', '\110', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\171', + '\165', '\131', '\040', '\161', '\165', '\040', '\061', '\012', + '\152', '\161', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\102', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\166', '\166', '\101', '\040', '\166', '\141', + '\040', '\061', '\012', '\145', '\126', '\152', '\040', '\145', + '\162', '\040', '\061', '\012', '\172', '\107', '\160', '\040', + '\163', '\172', '\040', '\061', '\012', '\166', '\143', '\102', + '\040', '\143', '\150', '\040', '\061', '\012', '\153', '\160', + '\110', '\040', '\153', '\141', '\040', '\061', '\012', '\155', + '\104', '\167', '\040', '\155', '\145', '\040', '\061', '\012', + '\166', '\165', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\126', '\171', '\040', '\156', '\171', '\040', + '\061', '\012', '\155', '\172', '\123', '\040', '\163', '\172', + '\040', '\061', '\012', '\152', '\166', '\115', '\040', '\151', + '\152', '\040', '\061', '\012', '\163', '\146', '\126', '\040', + '\163', '\164', '\040', '\061', '\012', '\150', '\121', '\161', + '\040', '\164', '\150', '\040', '\061', '\012', '\167', '\124', + '\155', '\040', '\155', '\145', '\040', '\061', '\012', '\120', + '\154', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\146', '\170', '\112', '\040', '\146', '\157', '\040', '\061', + '\012', '\161', '\121', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\106', '\156', '\167', '\040', '\141', '\156', + '\040', '\061', '\012', '\161', '\112', '\157', '\040', '\161', + '\165', '\040', '\061', '\012', '\116', '\163', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\114', '\152', '\170', + '\040', '\151', '\152', '\040', '\061', '\012', '\163', '\122', + '\142', '\040', '\163', '\164', '\040', '\061', '\012', '\160', + '\143', '\131', '\040', '\143', '\150', '\040', '\061', '\012', + '\166', '\126', '\155', '\040', '\166', '\141', '\040', '\061', + '\012', '\163', '\121', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\131', '\167', '\172', '\040', '\163', '\172', + '\040', '\061', '\012', '\150', '\161', '\112', '\040', '\164', + '\150', '\040', '\061', '\012', '\163', '\152', '\113', '\040', + '\163', '\164', '\040', '\061', '\012', '\132', '\153', '\163', + '\040', '\163', '\164', '\040', '\061', '\012', '\115', '\152', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\104', + '\167', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\167', '\142', '\116', '\040', '\167', '\141', '\040', '\061', + '\012', '\155', '\166', '\113', '\040', '\166', '\141', '\040', + '\061', '\012', '\162', '\114', '\160', '\040', '\145', '\162', + '\040', '\061', '\012', '\114', '\142', '\155', '\040', '\155', + '\145', '\040', '\061', '\012', '\167', '\152', '\117', '\040', + '\151', '\152', '\040', '\061', '\012', '\154', '\121', '\172', + '\040', '\154', '\145', '\040', '\061', '\012', '\113', '\167', + '\146', '\040', '\167', '\141', '\040', '\061', '\012', '\161', + '\155', '\102', '\040', '\161', '\165', '\040', '\061', '\012', + '\130', '\142', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\143', '\113', '\161', '\040', '\143', '\150', '\040', + '\061', '\012', '\150', '\161', '\122', '\040', '\164', '\150', + '\040', '\061', '\012', '\171', '\126', '\142', '\040', '\142', + '\145', '\040', '\061', '\012', '\170', '\143', '\106', '\040', + '\143', '\150', '\040', '\061', '\012', '\105', '\167', '\166', + '\040', '\166', '\141', '\040', '\061', '\012', '\107', '\160', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\107', + '\142', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\171', '\110', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\147', '\130', '\153', '\040', '\156', '\147', '\040', + '\061', '\012', '\161', '\117', '\170', '\040', '\161', '\165', + '\040', '\061', '\012', '\113', '\142', '\167', '\040', '\167', + '\141', '\040', '\061', '\012', '\161', '\110', '\170', '\040', + '\161', '\165', '\040', '\061', '\012', '\167', '\152', '\120', + '\040', '\151', '\152', '\040', '\061', '\012', '\152', '\121', + '\154', '\040', '\154', '\145', '\040', '\061', '\012', '\106', + '\146', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\157', '\131', '\142', '\040', '\157', '\156', '\040', '\061', + '\012', '\106', '\161', '\157', '\040', '\161', '\165', '\040', + '\061', '\012', '\167', '\130', '\172', '\040', '\163', '\172', + '\040', '\061', '\012', '\146', '\111', '\160', '\040', '\160', + '\162', '\040', '\061', '\012', '\160', '\115', '\146', '\040', + '\160', '\162', '\040', '\061', '\012', '\156', '\161', '\120', + '\040', '\141', '\156', '\040', '\061', '\012', '\142', '\142', + '\132', '\040', '\142', '\145', '\040', '\061', '\012', '\150', + '\163', '\130', '\040', '\164', '\150', '\040', '\061', '\012', + '\127', '\152', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\132', '\161', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\120', '\170', '\142', '\040', '\142', '\145', + '\040', '\061', '\012', '\102', '\172', '\163', '\040', '\163', + '\164', '\040', '\061', '\012', '\160', '\142', '\111', '\040', + '\160', '\162', '\040', '\061', '\012', '\131', '\166', '\160', + '\040', '\166', '\141', '\040', '\061', '\012', '\152', '\170', + '\115', '\040', '\151', '\152', '\040', '\061', '\012', '\152', + '\171', '\132', '\040', '\151', '\152', '\040', '\061', '\012', + '\155', '\172', '\112', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\131', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\161', '\115', '\155', '\040', '\161', '\165', + '\040', '\061', '\012', '\146', '\150', '\114', '\040', '\164', + '\150', '\040', '\061', '\012', '\161', '\117', '\147', '\040', + '\161', '\165', '\040', '\061', '\012', '\115', '\156', '\160', + '\040', '\141', '\156', '\040', '\061', '\012', '\111', '\146', + '\166', '\040', '\166', '\141', '\040', '\061', '\012', '\161', + '\131', '\155', '\040', '\161', '\165', '\040', '\061', '\012', + '\147', '\170', '\166', '\040', '\156', '\147', '\040', '\061', + '\012', '\172', '\146', '\107', '\040', '\163', '\172', '\040', + '\061', '\012', '\146', '\161', '\107', '\040', '\161', '\165', + '\040', '\061', '\012', '\154', '\114', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\150', '\153', '\113', '\040', + '\164', '\150', '\040', '\061', '\012', '\157', '\131', '\153', + '\040', '\157', '\156', '\040', '\061', '\012', '\154', '\122', + '\147', '\040', '\154', '\145', '\040', '\061', '\012', '\154', + '\117', '\170', '\040', '\154', '\145', '\040', '\061', '\012', + '\126', '\170', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\161', '\101', '\163', '\040', '\161', '\165', '\040', + '\061', '\012', '\164', '\113', '\153', '\040', '\164', '\150', + '\040', '\061', '\012', '\154', '\150', '\106', '\040', '\164', + '\150', '\040', '\061', '\012', '\144', '\103', '\166', '\040', + '\144', '\145', '\040', '\061', '\012', '\167', '\166', '\131', + '\040', '\166', '\141', '\040', '\061', '\012', '\167', '\151', + '\126', '\040', '\151', '\156', '\040', '\061', '\012', '\143', + '\162', '\106', '\040', '\143', '\150', '\040', '\061', '\012', + '\146', '\105', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\122', '\162', '\154', '\040', '\145', '\162', '\040', + '\061', '\012', '\132', '\152', '\171', '\040', '\151', '\152', + '\040', '\061', '\012', '\161', '\142', '\131', '\040', '\161', + '\165', '\040', '\061', '\012', '\153', '\115', '\167', '\040', + '\153', '\141', '\040', '\061', '\012', '\166', '\132', '\151', + '\040', '\151', '\156', '\040', '\061', '\012', '\106', '\170', + '\151', '\040', '\151', '\156', '\040', '\061', '\012', '\172', + '\153', '\123', '\040', '\163', '\172', '\040', '\061', '\012', + '\166', '\113', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\172', '\142', '\111', '\040', '\163', '\172', '\040', + '\061', '\012', '\165', '\110', '\147', '\040', '\161', '\165', + '\040', '\061', '\012', '\161', '\172', '\107', '\040', '\161', + '\165', '\040', '\061', '\012', '\152', '\115', '\153', '\040', + '\151', '\152', '\040', '\061', '\012', '\106', '\153', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\144', '\113', + '\155', '\040', '\144', '\145', '\040', '\061', '\012', '\156', + '\110', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\170', '\107', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\160', '\125', '\040', '\161', '\165', '\040', + '\061', '\012', '\162', '\143', '\125', '\040', '\143', '\150', + '\040', '\061', '\012', '\141', '\127', '\170', '\040', '\141', + '\156', '\040', '\061', '\012', '\170', '\144', '\123', '\040', + '\144', '\145', '\040', '\061', '\012', '\161', '\150', '\126', + '\040', '\164', '\150', '\040', '\061', '\012', '\141', '\110', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\166', + '\155', '\111', '\040', '\166', '\141', '\040', '\061', '\012', + '\127', '\143', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\102', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\153', '\121', '\145', '\040', '\145', '\162', + '\040', '\061', '\012', '\141', '\167', '\112', '\040', '\141', + '\156', '\040', '\061', '\012', '\170', '\144', '\104', '\040', + '\144', '\145', '\040', '\061', '\012', '\171', '\132', '\170', + '\040', '\156', '\171', '\040', '\061', '\012', '\113', '\153', + '\144', '\040', '\144', '\145', '\040', '\061', '\012', '\167', + '\102', '\172', '\040', '\163', '\172', '\040', '\061', '\012', + '\154', '\172', '\101', '\040', '\154', '\145', '\040', '\061', + '\012', '\171', '\171', '\124', '\040', '\156', '\171', '\040', + '\061', '\012', '\161', '\145', '\113', '\040', '\161', '\165', + '\040', '\061', '\012', '\172', '\160', '\105', '\040', '\163', + '\172', '\040', '\061', '\012', '\172', '\106', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\171', '\171', '\107', + '\040', '\156', '\171', '\040', '\061', '\012', '\154', '\114', + '\167', '\040', '\154', '\145', '\040', '\061', '\012', '\142', + '\166', '\123', '\040', '\166', '\141', '\040', '\061', '\012', + '\155', '\166', '\130', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\154', '\127', '\040', '\164', '\150', '\040', + '\061', '\012', '\160', '\147', '\130', '\040', '\156', '\147', + '\040', '\061', '\012', '\154', '\121', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\171', '\155', '\131', '\040', + '\155', '\145', '\040', '\061', '\012', '\155', '\152', '\112', + '\040', '\151', '\152', '\040', '\061', '\012', '\155', '\126', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\130', + '\161', '\163', '\040', '\161', '\165', '\040', '\061', '\012', + '\142', '\113', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\142', '\110', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\152', '\122', '\166', '\040', '\151', '\152', + '\040', '\061', '\012', '\114', '\160', '\167', '\040', '\160', + '\162', '\040', '\061', '\012', '\172', '\120', '\142', '\040', + '\163', '\172', '\040', '\061', '\012', '\167', '\153', '\122', + '\040', '\153', '\141', '\040', '\061', '\012', '\153', '\170', + '\123', '\040', '\153', '\141', '\040', '\061', '\012', '\152', + '\127', '\146', '\040', '\151', '\152', '\040', '\061', '\012', + '\116', '\153', '\170', '\040', '\153', '\141', '\040', '\061', + '\012', '\113', '\143', '\152', '\040', '\143', '\150', '\040', + '\061', '\012', '\142', '\112', '\142', '\040', '\142', '\145', + '\040', '\061', '\012', '\170', '\167', '\132', '\040', '\167', + '\141', '\040', '\061', '\012', '\122', '\161', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\121', '\172', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\152', '\167', + '\110', '\040', '\151', '\152', '\040', '\061', '\012', '\104', + '\161', '\144', '\040', '\161', '\165', '\040', '\061', '\012', + '\166', '\114', '\146', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\130', '\144', '\040', '\164', '\150', '\040', + '\061', '\012', '\143', '\146', '\104', '\040', '\143', '\150', + '\040', '\061', '\012', '\163', '\152', '\130', '\040', '\163', + '\164', '\040', '\061', '\012', '\150', '\172', '\111', '\040', + '\164', '\150', '\040', '\061', '\012', '\161', '\125', '\144', + '\040', '\161', '\165', '\040', '\061', '\012', '\164', '\123', + '\170', '\040', '\164', '\150', '\040', '\061', '\012', '\150', + '\170', '\101', '\040', '\164', '\150', '\040', '\061', '\012', + '\147', '\170', '\113', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\126', '\155', '\040', '\164', '\150', '\040', + '\061', '\012', '\171', '\172', '\130', '\040', '\163', '\172', + '\040', '\061', '\012', '\125', '\143', '\163', '\040', '\143', + '\150', '\040', '\061', '\012', '\161', '\141', '\110', '\040', + '\141', '\156', '\040', '\061', '\012', '\131', '\146', '\171', + '\040', '\156', '\171', '\040', '\061', '\012', '\163', '\112', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\151', + '\110', '\160', '\040', '\151', '\156', '\040', '\061', '\012', + '\151', '\171', '\103', '\040', '\151', '\156', '\040', '\061', + '\012', '\124', '\152', '\146', '\040', '\151', '\152', '\040', + '\061', '\012', '\144', '\112', '\160', '\040', '\144', '\145', + '\040', '\061', '\012', '\112', '\147', '\166', '\040', '\156', + '\147', '\040', '\061', '\012', '\165', '\112', '\146', '\040', + '\161', '\165', '\040', '\061', '\012', '\156', '\116', '\154', + '\040', '\141', '\156', '\040', '\061', '\012', '\172', '\144', + '\101', '\040', '\163', '\172', '\040', '\061', '\012', '\170', + '\111', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\161', '\152', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\172', '\131', '\040', '\163', '\172', '\040', + '\061', '\012', '\167', '\161', '\166', '\040', '\161', '\165', + '\040', '\061', '\012', '\130', '\166', '\170', '\040', '\166', + '\141', '\040', '\061', '\012', '\146', '\112', '\162', '\040', + '\145', '\162', '\040', '\061', '\012', '\156', '\161', '\110', + '\040', '\141', '\156', '\040', '\061', '\012', '\161', '\107', + '\144', '\040', '\161', '\165', '\040', '\061', '\012', '\166', + '\121', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\151', '\121', '\172', '\040', '\151', '\156', '\040', '\061', + '\012', '\164', '\114', '\156', '\040', '\164', '\150', '\040', + '\061', '\012', '\154', '\126', '\152', '\040', '\154', '\145', + '\040', '\061', '\012', '\166', '\161', '\127', '\040', '\161', + '\165', '\040', '\061', '\012', '\172', '\162', '\116', '\040', + '\145', '\162', '\040', '\061', '\012', '\170', '\113', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\167', '\141', + '\126', '\040', '\141', '\156', '\040', '\061', '\012', '\131', + '\144', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\144', '\153', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\103', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\130', '\143', '\171', '\040', '\143', '\150', + '\040', '\061', '\012', '\160', '\111', '\154', '\040', '\154', + '\145', '\040', '\061', '\012', '\150', '\130', '\154', '\040', + '\164', '\150', '\040', '\061', '\012', '\141', '\106', '\163', + '\040', '\141', '\156', '\040', '\061', '\012', '\151', '\167', + '\115', '\040', '\151', '\156', '\040', '\061', '\012', '\107', + '\167', '\170', '\040', '\167', '\141', '\040', '\061', '\012', + '\130', '\154', '\160', '\040', '\154', '\145', '\040', '\061', + '\012', '\121', '\146', '\165', '\040', '\161', '\165', '\040', + '\061', '\012', '\152', '\161', '\105', '\040', '\161', '\165', + '\040', '\061', '\012', '\154', '\161', '\120', '\040', '\161', + '\165', '\040', '\061', '\012', '\153', '\126', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\170', '\161', '\112', + '\040', '\161', '\165', '\040', '\061', '\012', '\115', '\172', + '\146', '\040', '\163', '\172', '\040', '\061', '\012', '\155', + '\116', '\167', '\040', '\155', '\145', '\040', '\061', '\012', + '\127', '\163', '\166', '\040', '\163', '\164', '\040', '\061', + '\012', '\146', '\156', '\115', '\040', '\141', '\156', '\040', + '\061', '\012', '\165', '\123', '\146', '\040', '\161', '\165', + '\040', '\061', '\012', '\150', '\103', '\146', '\040', '\164', + '\150', '\040', '\061', '\012', '\172', '\152', '\110', '\040', + '\163', '\172', '\040', '\061', '\012', '\155', '\124', '\163', + '\040', '\163', '\164', '\040', '\061', '\012', '\152', '\127', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\104', + '\170', '\153', '\040', '\153', '\141', '\040', '\061', '\012', + '\132', '\164', '\144', '\040', '\164', '\150', '\040', '\061', + '\012', '\122', '\166', '\166', '\040', '\166', '\141', '\040', + '\061', '\012', '\147', '\102', '\170', '\040', '\156', '\147', + '\040', '\061', '\012', '\114', '\172', '\170', '\040', '\163', + '\172', '\040', '\061', '\012', '\145', '\172', '\125', '\040', + '\145', '\162', '\040', '\061', '\012', '\152', '\161', '\110', + '\040', '\161', '\165', '\040', '\061', '\012', '\122', '\152', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\104', + '\143', '\147', '\040', '\143', '\150', '\040', '\061', '\012', + '\142', '\102', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\150', '\117', '\040', '\164', '\150', '\040', + '\061', '\012', '\150', '\160', '\110', '\040', '\164', '\150', + '\040', '\061', '\012', '\132', '\161', '\141', '\040', '\141', + '\156', '\040', '\061', '\012', '\153', '\103', '\170', '\040', + '\153', '\141', '\040', '\061', '\012', '\162', '\122', '\166', + '\040', '\145', '\162', '\040', '\061', '\012', '\144', '\153', + '\132', '\040', '\144', '\145', '\040', '\061', '\012', '\107', + '\147', '\170', '\040', '\156', '\147', '\040', '\061', '\012', + '\160', '\121', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\107', '\143', '\166', '\040', '\143', '\150', '\040', + '\061', '\012', '\123', '\143', '\147', '\040', '\143', '\150', + '\040', '\061', '\012', '\166', '\104', '\142', '\040', '\166', + '\141', '\040', '\061', '\012', '\160', '\142', '\104', '\040', + '\160', '\162', '\040', '\061', '\012', '\166', '\105', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\166', '\154', + '\105', '\040', '\154', '\145', '\040', '\061', '\012', '\122', + '\152', '\154', '\040', '\154', '\145', '\040', '\061', '\012', + '\154', '\106', '\167', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\161', '\116', '\040', '\161', '\165', '\040', + '\061', '\012', '\141', '\120', '\161', '\040', '\141', '\156', + '\040', '\061', '\012', '\147', '\152', '\104', '\040', '\156', + '\147', '\040', '\061', '\012', '\152', '\143', '\105', '\040', + '\143', '\150', '\040', '\061', '\012', '\167', '\123', '\167', + '\040', '\167', '\141', '\040', '\061', '\012', '\104', '\147', + '\152', '\040', '\156', '\147', '\040', '\061', '\012', '\150', + '\165', '\132', '\040', '\164', '\150', '\040', '\061', '\012', + '\147', '\120', '\166', '\040', '\156', '\147', '\040', '\061', + '\012', '\160', '\112', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\143', '\121', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\155', '\167', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\166', '\160', '\101', '\040', + '\166', '\141', '\040', '\061', '\012', '\150', '\107', '\146', + '\040', '\164', '\150', '\040', '\061', '\012', '\143', '\130', + '\172', '\040', '\143', '\150', '\040', '\061', '\012', '\114', + '\143', '\142', '\040', '\143', '\150', '\040', '\061', '\012', + '\146', '\112', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\121', '\172', '\171', '\040', '\163', '\172', '\040', + '\061', '\012', '\172', '\121', '\155', '\040', '\163', '\172', + '\040', '\061', '\012', '\110', '\150', '\156', '\040', '\164', + '\150', '\040', '\061', '\012', '\170', '\144', '\131', '\040', + '\144', '\145', '\040', '\061', '\012', '\165', '\131', '\154', + '\040', '\161', '\165', '\040', '\061', '\012', '\130', '\153', + '\152', '\040', '\151', '\152', '\040', '\061', '\012', '\152', + '\166', '\101', '\040', '\151', '\152', '\040', '\061', '\012', + '\112', '\166', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\151', '\167', '\132', '\040', '\151', '\156', '\040', + '\061', '\012', '\172', '\153', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\116', '\150', '\142', '\040', '\164', + '\150', '\040', '\061', '\012', '\153', '\155', '\126', '\040', + '\153', '\141', '\040', '\061', '\012', '\161', '\113', '\144', + '\040', '\161', '\165', '\040', '\061', '\012', '\102', '\143', + '\161', '\040', '\143', '\150', '\040', '\061', '\012', '\160', + '\146', '\131', '\040', '\160', '\162', '\040', '\061', '\012', + '\161', '\125', '\152', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\161', '\122', '\040', '\161', '\165', '\040', + '\061', '\012', '\147', '\167', '\117', '\040', '\156', '\147', + '\040', '\061', '\012', '\147', '\130', '\155', '\040', '\156', + '\147', '\040', '\061', '\012', '\152', '\110', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\162', '\102', '\156', + '\040', '\141', '\156', '\040', '\061', '\012', '\165', '\120', + '\167', '\040', '\161', '\165', '\040', '\061', '\012', '\160', + '\112', '\153', '\040', '\153', '\141', '\040', '\061', '\012', + '\111', '\160', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\161', '\115', '\040', '\161', '\165', '\040', + '\061', '\012', '\131', '\161', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\113', '\142', '\172', '\040', '\163', + '\172', '\040', '\061', '\012', '\166', '\146', '\114', '\040', + '\166', '\141', '\040', '\061', '\012', '\156', '\160', '\132', + '\040', '\141', '\156', '\040', '\061', '\012', '\157', '\161', + '\131', '\040', '\161', '\165', '\040', '\061', '\012', '\132', + '\161', '\146', '\040', '\161', '\165', '\040', '\061', '\012', + '\152', '\172', '\125', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\116', '\170', '\040', '\166', '\141', '\040', + '\061', '\012', '\150', '\130', '\146', '\040', '\164', '\150', + '\040', '\061', '\012', '\146', '\103', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\156', '\172', '\112', '\040', + '\141', '\156', '\040', '\061', '\012', '\155', '\113', '\152', + '\040', '\151', '\152', '\040', '\061', '\012', '\167', '\155', + '\102', '\040', '\155', '\145', '\040', '\061', '\012', '\127', + '\152', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\104', '\142', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\130', '\171', '\040', '\163', '\172', '\040', + '\061', '\012', '\170', '\131', '\167', '\040', '\167', '\141', + '\040', '\061', '\012', '\146', '\121', '\146', '\040', '\146', + '\157', '\040', '\061', '\012', '\144', '\161', '\120', '\040', + '\161', '\165', '\040', '\061', '\012', '\113', '\170', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\152', '\144', + '\132', '\040', '\144', '\145', '\040', '\061', '\012', '\161', + '\162', '\130', '\040', '\161', '\165', '\040', '\061', '\012', + '\114', '\170', '\142', '\040', '\142', '\145', '\040', '\061', + '\012', '\171', '\146', '\114', '\040', '\156', '\171', '\040', + '\061', '\012', '\171', '\131', '\155', '\040', '\155', '\145', + '\040', '\061', '\012', '\163', '\142', '\110', '\040', '\163', + '\164', '\040', '\061', '\012', '\167', '\154', '\126', '\040', + '\154', '\145', '\040', '\061', '\012', '\165', '\113', '\160', + '\040', '\161', '\165', '\040', '\061', '\012', '\150', '\150', + '\116', '\040', '\164', '\150', '\040', '\061', '\012', '\130', + '\170', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\152', '\114', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\156', '\121', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\127', '\161', '\160', '\040', '\161', '\165', + '\040', '\061', '\012', '\116', '\161', '\144', '\040', '\161', + '\165', '\040', '\061', '\012', '\152', '\146', '\104', '\040', + '\151', '\152', '\040', '\061', '\012', '\112', '\156', '\161', + '\040', '\141', '\156', '\040', '\061', '\012', '\102', '\172', + '\156', '\040', '\141', '\156', '\040', '\061', '\012', '\155', + '\112', '\162', '\040', '\145', '\162', '\040', '\061', '\012', + '\161', '\141', '\130', '\040', '\141', '\156', '\040', '\061', + '\012', '\160', '\112', '\167', '\040', '\160', '\162', '\040', + '\061', '\012', '\152', '\110', '\172', '\040', '\163', '\172', + '\040', '\061', '\012', '\171', '\141', '\130', '\040', '\141', + '\156', '\040', '\061', '\012', '\127', '\150', '\163', '\040', + '\164', '\150', '\040', '\061', '\012', '\150', '\131', '\162', + '\040', '\164', '\150', '\040', '\061', '\012', '\164', '\155', + '\123', '\040', '\164', '\150', '\040', '\061', '\012', '\106', + '\150', '\171', '\040', '\164', '\150', '\040', '\061', '\012', + '\107', '\147', '\144', '\040', '\156', '\147', '\040', '\061', + '\012', '\130', '\155', '\171', '\040', '\155', '\145', '\040', + '\061', '\012', '\122', '\161', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\106', '\163', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\161', '\150', '\101', '\040', + '\164', '\150', '\040', '\061', '\012', '\146', '\150', '\130', + '\040', '\164', '\150', '\040', '\061', '\012', '\110', '\161', + '\170', '\040', '\161', '\165', '\040', '\061', '\012', '\167', + '\111', '\157', '\040', '\157', '\156', '\040', '\061', '\012', + '\111', '\142', '\170', '\040', '\142', '\145', '\040', '\061', + '\012', '\143', '\106', '\170', '\040', '\143', '\150', '\040', + '\061', '\012', '\144', '\122', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\163', '\156', '\126', '\040', '\141', + '\156', '\040', '\061', '\012', '\153', '\161', '\172', '\040', + '\161', '\165', '\040', '\061', '\012', '\145', '\161', '\117', + '\040', '\145', '\162', '\040', '\061', '\012', '\107', '\153', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\116', + '\156', '\172', '\040', '\141', '\156', '\040', '\061', '\012', + '\171', '\161', '\105', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\112', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\170', '\166', '\101', '\040', '\166', '\141', + '\040', '\061', '\012', '\161', '\115', '\170', '\040', '\161', + '\165', '\040', '\061', '\012', '\144', '\167', '\123', '\040', + '\144', '\145', '\040', '\061', '\012', '\171', '\101', '\152', + '\040', '\151', '\152', '\040', '\061', '\012', '\170', '\103', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\147', + '\155', '\105', '\040', '\156', '\147', '\040', '\061', '\012', + '\142', '\150', '\120', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\167', '\105', '\040', '\145', '\162', '\040', + '\061', '\012', '\130', '\156', '\172', '\040', '\141', '\156', + '\040', '\061', '\012', '\125', '\150', '\167', '\040', '\164', + '\150', '\040', '\061', '\012', '\170', '\156', '\122', '\040', + '\141', '\156', '\040', '\061', '\012', '\156', '\146', '\132', + '\040', '\141', '\156', '\040', '\061', '\012', '\121', '\160', + '\170', '\040', '\160', '\162', '\040', '\061', '\012', '\161', + '\170', '\117', '\040', '\161', '\165', '\040', '\061', '\012', + '\154', '\107', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\161', '\122', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\122', '\167', '\170', '\040', '\167', '\141', + '\040', '\061', '\012', '\164', '\143', '\115', '\040', '\164', + '\150', '\040', '\061', '\012', '\146', '\102', '\144', '\040', + '\144', '\145', '\040', '\061', '\012', '\122', '\152', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\144', '\146', + '\131', '\040', '\144', '\145', '\040', '\061', '\012', '\150', + '\150', '\122', '\040', '\164', '\150', '\040', '\061', '\012', + '\142', '\103', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\146', '\161', '\114', '\040', '\161', '\165', '\040', + '\061', '\012', '\154', '\172', '\123', '\040', '\154', '\145', + '\040', '\061', '\012', '\114', '\162', '\155', '\040', '\145', + '\162', '\040', '\061', '\012', '\145', '\161', '\105', '\040', + '\161', '\165', '\040', '\061', '\012', '\166', '\147', '\114', + '\040', '\156', '\147', '\040', '\061', '\012', '\167', '\121', + '\162', '\040', '\145', '\162', '\040', '\061', '\012', '\142', + '\167', '\102', '\040', '\167', '\141', '\040', '\061', '\012', + '\154', '\107', '\146', '\040', '\154', '\145', '\040', '\061', + '\012', '\116', '\167', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\163', '\144', '\125', '\040', '\163', '\164', + '\040', '\061', '\012', '\132', '\170', '\166', '\040', '\166', + '\141', '\040', '\061', '\012', '\171', '\104', '\155', '\040', + '\155', '\145', '\040', '\061', '\012', '\114', '\163', '\167', + '\040', '\163', '\164', '\040', '\061', '\012', '\143', '\116', + '\161', '\040', '\143', '\150', '\040', '\061', '\012', '\104', + '\161', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\166', '\114', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\144', '\127', '\166', '\040', '\144', '\145', '\040', + '\061', '\012', '\146', '\153', '\121', '\040', '\153', '\141', + '\040', '\061', '\012', '\172', '\152', '\104', '\040', '\163', + '\172', '\040', '\061', '\012', '\171', '\131', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\161', '\145', '\124', + '\040', '\161', '\165', '\040', '\061', '\012', '\143', '\166', + '\114', '\040', '\143', '\150', '\040', '\061', '\012', '\167', + '\153', '\101', '\040', '\153', '\141', '\040', '\061', '\012', + '\116', '\166', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\144', '\152', '\115', '\040', '\144', '\145', '\040', + '\061', '\012', '\150', '\147', '\113', '\040', '\164', '\150', + '\040', '\061', '\012', '\160', '\130', '\142', '\040', '\160', + '\162', '\040', '\061', '\012', '\124', '\154', '\167', '\040', + '\154', '\145', '\040', '\061', '\012', '\122', '\150', '\172', + '\040', '\150', '\141', '\040', '\061', '\012', '\167', '\153', + '\120', '\040', '\153', '\141', '\040', '\061', '\012', '\167', + '\104', '\153', '\040', '\153', '\141', '\040', '\061', '\012', + '\145', '\106', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\145', '\150', '\125', '\040', '\164', '\150', '\040', + '\061', '\012', '\130', '\154', '\171', '\040', '\154', '\145', + '\040', '\061', '\012', '\167', '\170', '\113', '\040', '\167', + '\141', '\040', '\061', '\012', '\144', '\120', '\167', '\040', + '\144', '\145', '\040', '\061', '\012', '\163', '\106', '\144', + '\040', '\163', '\164', '\040', '\061', '\012', '\166', '\143', + '\111', '\040', '\143', '\150', '\040', '\061', '\012', '\106', + '\170', '\144', '\040', '\144', '\145', '\040', '\061', '\012', + '\146', '\166', '\122', '\040', '\166', '\141', '\040', '\061', + '\012', '\152', '\161', '\163', '\040', '\161', '\165', '\040', + '\061', '\012', '\162', '\115', '\152', '\040', '\145', '\162', + '\040', '\061', '\012', '\161', '\142', '\127', '\040', '\161', + '\165', '\040', '\061', '\012', '\153', '\160', '\120', '\040', + '\153', '\141', '\040', '\061', '\012', '\102', '\166', '\167', + '\040', '\166', '\141', '\040', '\061', '\012', '\124', '\155', + '\153', '\040', '\153', '\141', '\040', '\061', '\012', '\150', + '\142', '\120', '\040', '\164', '\150', '\040', '\061', '\012', + '\150', '\115', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\147', '\114', '\040', '\156', '\147', '\040', + '\061', '\012', '\145', '\146', '\125', '\040', '\145', '\162', + '\040', '\061', '\012', '\143', '\121', '\142', '\040', '\143', + '\150', '\040', '\061', '\012', '\155', '\143', '\101', '\040', + '\143', '\150', '\040', '\061', '\012', '\105', '\167', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\170', '\155', + '\126', '\040', '\155', '\145', '\040', '\061', '\012', '\121', + '\143', '\161', '\040', '\143', '\150', '\040', '\061', '\012', + '\155', '\172', '\107', '\040', '\163', '\172', '\040', '\061', + '\012', '\160', '\113', '\155', '\040', '\155', '\145', '\040', + '\061', '\012', '\106', '\167', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\154', '\122', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\152', '\120', '\153', '\040', + '\151', '\152', '\040', '\061', '\012', '\152', '\115', '\142', + '\040', '\151', '\152', '\040', '\061', '\012', '\155', '\172', + '\117', '\040', '\163', '\172', '\040', '\061', '\012', '\157', + '\106', '\167', '\040', '\157', '\156', '\040', '\061', '\012', + '\150', '\112', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\126', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\151', '\126', '\172', '\040', '\151', '\156', + '\040', '\061', '\012', '\157', '\161', '\125', '\040', '\161', + '\165', '\040', '\061', '\012', '\142', '\150', '\127', '\040', + '\164', '\150', '\040', '\061', '\012', '\117', '\170', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\155', '\121', + '\153', '\040', '\153', '\141', '\040', '\061', '\012', '\130', + '\146', '\142', '\040', '\142', '\145', '\040', '\061', '\012', + '\143', '\116', '\167', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\147', '\132', '\040', '\156', '\147', '\040', + '\061', '\012', '\124', '\166', '\146', '\040', '\166', '\141', + '\040', '\061', '\012', '\163', '\111', '\170', '\040', '\163', + '\164', '\040', '\061', '\012', '\165', '\132', '\163', '\040', + '\161', '\165', '\040', '\061', '\012', '\170', '\172', '\130', + '\040', '\163', '\172', '\040', '\061', '\012', '\131', '\154', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\157', + '\110', '\146', '\040', '\157', '\156', '\040', '\061', '\012', + '\143', '\163', '\125', '\040', '\143', '\150', '\040', '\061', + '\012', '\121', '\172', '\163', '\040', '\163', '\164', '\040', + '\061', '\012', '\102', '\146', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\171', '\112', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\160', '\147', '\121', '\040', + '\156', '\147', '\040', '\061', '\012', '\167', '\170', '\153', + '\040', '\153', '\141', '\040', '\061', '\012', '\124', '\156', + '\167', '\040', '\141', '\156', '\040', '\061', '\012', '\142', + '\113', '\170', '\040', '\142', '\145', '\040', '\061', '\012', + '\142', '\161', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\152', '\163', '\040', '\163', '\164', '\040', + '\061', '\012', '\160', '\106', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\130', '\166', '\154', '\040', '\154', + '\145', '\040', '\061', '\012', '\153', '\146', '\102', '\040', + '\153', '\141', '\040', '\061', '\012', '\155', '\132', '\154', + '\040', '\154', '\145', '\040', '\061', '\012', '\103', '\163', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\166', + '\162', '\112', '\040', '\145', '\162', '\040', '\061', '\012', + '\107', '\146', '\171', '\040', '\156', '\171', '\040', '\061', + '\012', '\152', '\142', '\120', '\040', '\151', '\152', '\040', + '\061', '\012', '\131', '\166', '\154', '\040', '\154', '\145', + '\040', '\061', '\012', '\110', '\170', '\142', '\040', '\142', + '\145', '\040', '\061', '\012', '\154', '\162', '\104', '\040', + '\145', '\162', '\040', '\061', '\012', '\161', '\124', '\154', + '\040', '\161', '\165', '\040', '\061', '\012', '\141', '\102', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\146', + '\107', '\142', '\040', '\142', '\145', '\040', '\061', '\012', + '\155', '\150', '\123', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\124', '\160', '\040', '\163', '\172', '\040', + '\061', '\012', '\153', '\122', '\144', '\040', '\144', '\145', + '\040', '\061', '\012', '\127', '\160', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\116', '\160', '\152', '\040', + '\151', '\152', '\040', '\061', '\012', '\154', '\167', '\123', + '\040', '\154', '\145', '\040', '\061', '\012', '\155', '\107', + '\155', '\040', '\155', '\145', '\040', '\061', '\012', '\156', + '\161', '\124', '\040', '\141', '\156', '\040', '\061', '\012', + '\125', '\152', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\152', '\117', '\040', '\151', '\152', '\040', + '\061', '\012', '\144', '\115', '\172', '\040', '\163', '\172', + '\040', '\061', '\012', '\167', '\113', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\171', '\132', '\162', '\040', + '\145', '\162', '\040', '\061', '\012', '\116', '\152', '\142', + '\040', '\151', '\152', '\040', '\061', '\012', '\131', '\154', + '\162', '\040', '\145', '\162', '\040', '\061', '\012', '\155', + '\126', '\146', '\040', '\155', '\145', '\040', '\061', '\012', + '\147', '\132', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\110', '\143', '\142', '\040', '\143', '\150', '\040', + '\061', '\012', '\170', '\143', '\102', '\040', '\143', '\150', + '\040', '\061', '\012', '\153', '\115', '\155', '\040', '\153', + '\141', '\040', '\061', '\012', '\154', '\167', '\103', '\040', + '\154', '\145', '\040', '\061', '\012', '\104', '\156', '\146', + '\040', '\141', '\156', '\040', '\061', '\012', '\150', '\152', + '\127', '\040', '\164', '\150', '\040', '\061', '\012', '\162', + '\124', '\153', '\040', '\145', '\162', '\040', '\061', '\012', + '\126', '\172', '\152', '\040', '\163', '\172', '\040', '\061', + '\012', '\126', '\170', '\171', '\040', '\156', '\171', '\040', + '\061', '\012', '\167', '\154', '\121', '\040', '\154', '\145', + '\040', '\061', '\012', '\116', '\162', '\166', '\040', '\145', + '\162', '\040', '\061', '\012', '\160', '\152', '\120', '\040', + '\151', '\152', '\040', '\061', '\012', '\146', '\167', '\132', + '\040', '\167', '\141', '\040', '\061', '\012', '\164', '\156', + '\127', '\040', '\164', '\150', '\040', '\061', '\012', '\157', + '\112', '\167', '\040', '\157', '\156', '\040', '\061', '\012', + '\153', '\112', '\170', '\040', '\153', '\141', '\040', '\061', + '\012', '\126', '\160', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\161', '\101', '\167', '\040', '\161', '\165', + '\040', '\061', '\012', '\121', '\150', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\142', '\103', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\166', '\162', '\125', + '\040', '\145', '\162', '\040', '\061', '\012', '\150', '\122', + '\143', '\040', '\164', '\150', '\040', '\061', '\012', '\143', + '\154', '\103', '\040', '\143', '\150', '\040', '\061', '\012', + '\162', '\106', '\144', '\040', '\145', '\162', '\040', '\061', + '\012', '\164', '\167', '\110', '\040', '\164', '\150', '\040', + '\061', '\012', '\153', '\103', '\167', '\040', '\153', '\141', + '\040', '\061', '\012', '\155', '\123', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\130', '\156', '\167', '\040', + '\141', '\156', '\040', '\061', '\012', '\146', '\130', '\155', + '\040', '\155', '\145', '\040', '\061', '\012', '\124', '\167', + '\146', '\040', '\167', '\141', '\040', '\061', '\012', '\106', + '\167', '\152', '\040', '\151', '\152', '\040', '\061', '\012', + '\142', '\152', '\112', '\040', '\151', '\152', '\040', '\061', + '\012', '\154', '\142', '\121', '\040', '\154', '\145', '\040', + '\061', '\012', '\153', '\166', '\123', '\040', '\153', '\141', + '\040', '\061', '\012', '\123', '\155', '\172', '\040', '\163', + '\172', '\040', '\061', '\012', '\146', '\102', '\160', '\040', + '\160', '\162', '\040', '\061', '\012', '\116', '\172', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\142', '\121', + '\160', '\040', '\160', '\162', '\040', '\061', '\012', '\166', + '\114', '\170', '\040', '\166', '\141', '\040', '\061', '\012', + '\150', '\126', '\146', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\125', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\143', '\132', '\144', '\040', '\143', '\150', + '\040', '\061', '\012', '\147', '\111', '\171', '\040', '\145', + '\147', '\040', '\061', '\012', '\150', '\126', '\161', '\040', + '\164', '\150', '\040', '\061', '\012', '\141', '\121', '\170', + '\040', '\141', '\156', '\040', '\061', '\012', '\121', '\146', + '\166', '\040', '\166', '\141', '\040', '\061', '\012', '\154', + '\113', '\142', '\040', '\154', '\145', '\040', '\061', '\012', + '\172', '\150', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\142', '\155', '\040', '\155', '\145', '\040', + '\061', '\012', '\107', '\143', '\161', '\040', '\143', '\150', + '\040', '\061', '\012', '\147', '\142', '\124', '\040', '\156', + '\147', '\040', '\061', '\012', '\160', '\131', '\153', '\040', + '\153', '\141', '\040', '\061', '\012', '\130', '\166', '\144', + '\040', '\144', '\145', '\040', '\061', '\012', '\170', '\115', + '\154', '\040', '\154', '\145', '\040', '\061', '\012', '\165', + '\110', '\142', '\040', '\161', '\165', '\040', '\061', '\012', + '\142', '\130', '\146', '\040', '\142', '\145', '\040', '\061', + '\012', '\163', '\116', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\161', '\126', '\171', '\040', '\161', '\165', + '\040', '\061', '\012', '\143', '\160', '\117', '\040', '\143', + '\150', '\040', '\061', '\012', '\106', '\147', '\142', '\040', + '\156', '\147', '\040', '\061', '\012', '\145', '\127', '\154', + '\040', '\145', '\162', '\040', '\061', '\012', '\153', '\113', + '\144', '\040', '\144', '\145', '\040', '\061', '\012', '\103', + '\142', '\152', '\040', '\151', '\152', '\040', '\061', '\012', + '\155', '\146', '\110', '\040', '\155', '\145', '\040', '\061', + '\012', '\161', '\111', '\141', '\040', '\141', '\156', '\040', + '\061', '\012', '\163', '\146', '\130', '\040', '\163', '\164', + '\040', '\061', '\012', '\163', '\156', '\110', '\040', '\141', + '\156', '\040', '\061', '\012', '\110', '\152', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\114', '\155', '\146', + '\040', '\155', '\145', '\040', '\061', '\012', '\170', '\147', + '\146', '\040', '\156', '\147', '\040', '\061', '\012', '\105', + '\166', '\167', '\040', '\166', '\141', '\040', '\061', '\012', + '\167', '\117', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\110', '\152', '\146', '\040', '\151', '\152', '\040', + '\061', '\012', '\172', '\165', '\112', '\040', '\161', '\165', + '\040', '\061', '\012', '\146', '\132', '\155', '\040', '\155', + '\145', '\040', '\061', '\012', '\154', '\116', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\170', '\125', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\156', '\114', + '\163', '\040', '\141', '\156', '\040', '\061', '\012', '\152', + '\153', '\123', '\040', '\151', '\152', '\040', '\061', '\012', + '\107', '\166', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\152', '\120', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\171', '\167', '\121', '\040', '\167', '\141', + '\040', '\061', '\012', '\161', '\162', '\107', '\040', '\161', + '\165', '\040', '\061', '\012', '\142', '\142', '\110', '\040', + '\142', '\145', '\040', '\061', '\012', '\147', '\150', '\112', + '\040', '\164', '\150', '\040', '\061', '\012', '\155', '\115', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\131', + '\166', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\170', '\114', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\102', '\144', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\172', '\112', '\144', '\040', '\163', '\172', + '\040', '\061', '\012', '\170', '\122', '\163', '\040', '\163', + '\164', '\040', '\061', '\012', '\166', '\147', '\120', '\040', + '\156', '\147', '\040', '\061', '\012', '\110', '\150', '\142', + '\040', '\164', '\150', '\040', '\061', '\012', '\156', '\160', + '\114', '\040', '\141', '\156', '\040', '\061', '\012', '\166', + '\106', '\160', '\040', '\166', '\141', '\040', '\061', '\012', + '\150', '\123', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\144', '\103', '\040', '\144', '\145', '\040', + '\061', '\012', '\153', '\107', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\153', '\126', '\146', '\040', '\153', + '\141', '\040', '\061', '\012', '\161', '\166', '\120', '\040', + '\161', '\165', '\040', '\061', '\012', '\153', '\167', '\117', + '\040', '\153', '\141', '\040', '\061', '\012', '\112', '\161', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\172', + '\127', '\170', '\040', '\163', '\172', '\040', '\061', '\012', + '\163', '\121', '\153', '\040', '\163', '\164', '\040', '\061', + '\012', '\150', '\156', '\126', '\040', '\164', '\150', '\040', + '\061', '\012', '\162', '\162', '\104', '\040', '\145', '\162', + '\040', '\061', '\012', '\152', '\126', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\166', '\166', '\131', '\040', + '\166', '\141', '\040', '\061', '\012', '\142', '\146', '\111', + '\040', '\142', '\145', '\040', '\061', '\012', '\146', '\123', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\103', + '\172', '\146', '\040', '\163', '\172', '\040', '\061', '\012', + '\153', '\127', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\152', '\112', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\107', '\167', '\152', '\040', '\151', '\152', + '\040', '\061', '\012', '\154', '\106', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\126', '\160', '\146', '\040', + '\146', '\157', '\040', '\061', '\012', '\146', '\153', '\126', + '\040', '\153', '\141', '\040', '\061', '\012', '\143', '\131', + '\152', '\040', '\143', '\150', '\040', '\061', '\012', '\155', + '\162', '\127', '\040', '\145', '\162', '\040', '\061', '\012', + '\150', '\102', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\112', '\170', '\040', '\164', '\150', '\040', + '\061', '\012', '\167', '\111', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\143', '\144', '\101', '\040', '\143', + '\150', '\040', '\061', '\012', '\167', '\121', '\171', '\040', + '\167', '\141', '\040', '\061', '\012', '\167', '\103', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\167', '\161', + '\132', '\040', '\161', '\165', '\040', '\061', '\012', '\152', + '\146', '\130', '\040', '\151', '\152', '\040', '\061', '\012', + '\152', '\164', '\107', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\153', '\112', '\040', '\153', '\141', '\040', + '\061', '\012', '\121', '\172', '\146', '\040', '\163', '\172', + '\040', '\061', '\012', '\147', '\113', '\163', '\040', '\156', + '\147', '\040', '\061', '\012', '\121', '\172', '\157', '\040', + '\157', '\156', '\040', '\061', '\012', '\142', '\167', '\111', + '\040', '\167', '\141', '\040', '\061', '\012', '\124', '\163', + '\142', '\040', '\163', '\164', '\040', '\061', '\012', '\166', + '\166', '\130', '\040', '\166', '\141', '\040', '\061', '\012', + '\152', '\154', '\122', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\154', '\121', '\040', '\161', '\165', '\040', + '\061', '\012', '\144', '\142', '\130', '\040', '\144', '\145', + '\040', '\061', '\012', '\110', '\146', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\102', '\163', '\152', '\040', + '\163', '\164', '\040', '\061', '\012', '\131', '\161', '\153', + '\040', '\161', '\165', '\040', '\061', '\012', '\130', '\156', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\142', + '\172', '\132', '\040', '\163', '\172', '\040', '\061', '\012', + '\144', '\107', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\147', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\152', '\167', '\105', '\040', '\167', '\141', + '\040', '\061', '\012', '\117', '\171', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\160', '\121', '\144', '\040', + '\144', '\145', '\040', '\061', '\012', '\152', '\122', '\171', + '\040', '\151', '\152', '\040', '\061', '\012', '\160', '\155', + '\130', '\040', '\155', '\145', '\040', '\061', '\012', '\154', + '\132', '\170', '\040', '\154', '\145', '\040', '\061', '\012', + '\147', '\106', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\155', '\112', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\163', '\113', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\111', '\153', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\172', '\153', '\107', '\040', + '\163', '\172', '\040', '\061', '\012', '\167', '\107', '\146', + '\040', '\167', '\141', '\040', '\061', '\012', '\161', '\122', + '\160', '\040', '\161', '\165', '\040', '\061', '\012', '\170', + '\104', '\156', '\040', '\141', '\156', '\040', '\061', '\012', + '\147', '\166', '\114', '\040', '\156', '\147', '\040', '\061', + '\012', '\155', '\107', '\170', '\040', '\155', '\145', '\040', + '\061', '\012', '\151', '\111', '\152', '\040', '\151', '\156', + '\040', '\061', '\012', '\107', '\172', '\144', '\040', '\163', + '\172', '\040', '\061', '\012', '\142', '\114', '\170', '\040', + '\142', '\145', '\040', '\061', '\012', '\152', '\125', '\154', + '\040', '\154', '\145', '\040', '\061', '\012', '\121', '\166', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\155', + '\126', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\165', '\150', '\106', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\126', '\153', '\040', '\153', '\141', '\040', + '\061', '\012', '\143', '\156', '\115', '\040', '\143', '\150', + '\040', '\061', '\012', '\165', '\106', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\155', '\130', '\146', '\040', + '\155', '\145', '\040', '\061', '\012', '\162', '\103', '\142', + '\040', '\145', '\162', '\040', '\061', '\012', '\156', '\114', + '\167', '\040', '\141', '\156', '\040', '\061', '\012', '\166', + '\146', '\110', '\040', '\146', '\157', '\040', '\061', '\012', + '\151', '\161', '\126', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\150', '\104', '\040', '\164', '\150', '\040', + '\061', '\012', '\163', '\110', '\170', '\040', '\163', '\164', + '\040', '\061', '\012', '\131', '\167', '\171', '\040', '\167', + '\141', '\040', '\061', '\012', '\155', '\104', '\170', '\040', + '\155', '\145', '\040', '\061', '\012', '\143', '\102', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\102', '\155', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\170', + '\122', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\142', '\123', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\166', '\103', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\124', '\143', '\166', '\040', '\143', '\150', + '\040', '\061', '\012', '\141', '\132', '\161', '\040', '\141', + '\156', '\040', '\061', '\012', '\112', '\143', '\170', '\040', + '\143', '\150', '\040', '\061', '\012', '\156', '\142', '\106', + '\040', '\141', '\156', '\040', '\061', '\012', '\121', '\172', + '\142', '\040', '\163', '\172', '\040', '\061', '\012', '\166', + '\153', '\121', '\040', '\153', '\141', '\040', '\061', '\012', + '\150', '\172', '\104', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\110', '\160', '\040', '\160', '\162', '\040', + '\061', '\012', '\150', '\161', '\130', '\040', '\164', '\150', + '\040', '\061', '\012', '\146', '\105', '\166', '\040', '\166', + '\141', '\040', '\061', '\012', '\171', '\152', '\106', '\040', + '\151', '\152', '\040', '\061', '\012', '\120', '\152', '\153', + '\040', '\151', '\152', '\040', '\061', '\012', '\163', '\146', + '\125', '\040', '\163', '\164', '\040', '\061', '\012', '\142', + '\107', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\155', '\143', '\130', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\130', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\171', '\166', '\123', '\040', '\166', '\141', + '\040', '\061', '\012', '\160', '\115', '\154', '\040', '\154', + '\145', '\040', '\061', '\012', '\167', '\112', '\163', '\040', + '\163', '\164', '\040', '\061', '\012', '\126', '\167', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\171', '\103', + '\167', '\040', '\167', '\141', '\040', '\061', '\012', '\161', + '\144', '\163', '\040', '\161', '\165', '\040', '\061', '\012', + '\162', '\122', '\152', '\040', '\145', '\162', '\040', '\061', + '\012', '\121', '\150', '\166', '\040', '\164', '\150', '\040', + '\061', '\012', '\165', '\143', '\107', '\040', '\143', '\150', + '\040', '\061', '\012', '\157', '\105', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\167', '\121', '\151', '\040', + '\151', '\156', '\040', '\061', '\012', '\154', '\123', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\114', '\161', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\156', + '\154', '\110', '\040', '\141', '\156', '\040', '\061', '\012', + '\165', '\161', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\117', '\141', '\157', '\040', '\141', '\156', '\040', + '\061', '\012', '\150', '\154', '\130', '\040', '\164', '\150', + '\040', '\061', '\012', '\146', '\120', '\167', '\040', '\167', + '\141', '\040', '\061', '\012', '\164', '\111', '\142', '\040', + '\164', '\150', '\040', '\061', '\012', '\172', '\111', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\161', '\155', + '\107', '\040', '\161', '\165', '\040', '\061', '\012', '\170', + '\112', '\155', '\040', '\155', '\145', '\040', '\061', '\012', + '\126', '\147', '\167', '\040', '\156', '\147', '\040', '\061', + '\012', '\125', '\153', '\170', '\040', '\153', '\141', '\040', + '\061', '\012', '\172', '\164', '\110', '\040', '\164', '\150', + '\040', '\061', '\012', '\154', '\150', '\120', '\040', '\164', + '\150', '\040', '\061', '\012', '\112', '\164', '\153', '\040', + '\164', '\150', '\040', '\061', '\012', '\110', '\172', '\144', + '\040', '\163', '\172', '\040', '\061', '\012', '\171', '\170', + '\121', '\040', '\156', '\171', '\040', '\061', '\012', '\156', + '\162', '\120', '\040', '\141', '\156', '\040', '\061', '\012', + '\146', '\110', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\131', '\143', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\107', '\161', '\141', '\040', '\141', '\156', + '\040', '\061', '\012', '\106', '\147', '\171', '\040', '\156', + '\147', '\040', '\061', '\012', '\157', '\102', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\166', '\165', '\103', + '\040', '\161', '\165', '\040', '\061', '\012', '\102', '\156', + '\172', '\040', '\141', '\156', '\040', '\061', '\012', '\166', + '\120', '\165', '\040', '\161', '\165', '\040', '\061', '\012', + '\170', '\106', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\152', '\144', '\112', '\040', '\144', '\145', '\040', + '\061', '\012', '\146', '\107', '\146', '\040', '\146', '\157', + '\040', '\061', '\012', '\131', '\152', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\121', '\152', '\160', '\040', + '\151', '\152', '\040', '\061', '\012', '\170', '\124', '\152', + '\040', '\151', '\152', '\040', '\061', '\012', '\166', '\117', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\166', + '\114', '\167', '\040', '\166', '\141', '\040', '\061', '\012', + '\163', '\115', '\146', '\040', '\163', '\164', '\040', '\061', + '\012', '\157', '\126', '\154', '\040', '\157', '\156', '\040', + '\061', '\012', '\143', '\167', '\116', '\040', '\143', '\150', + '\040', '\061', '\012', '\163', '\147', '\122', '\040', '\156', + '\147', '\040', '\061', '\012', '\152', '\152', '\121', '\040', + '\151', '\152', '\040', '\061', '\012', '\167', '\172', '\122', + '\040', '\163', '\172', '\040', '\061', '\012', '\172', '\150', + '\131', '\040', '\164', '\150', '\040', '\061', '\012', '\166', + '\142', '\122', '\040', '\166', '\141', '\040', '\061', '\012', + '\167', '\147', '\127', '\040', '\156', '\147', '\040', '\061', + '\012', '\161', '\167', '\130', '\040', '\161', '\165', '\040', + '\061', '\012', '\116', '\170', '\167', '\040', '\167', '\141', + '\040', '\061', '\012', '\145', '\121', '\157', '\040', '\145', + '\162', '\040', '\061', '\012', '\155', '\121', '\160', '\040', + '\155', '\145', '\040', '\061', '\012', '\113', '\161', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\164', '\166', + '\101', '\040', '\164', '\150', '\040', '\061', '\012', '\144', + '\154', '\112', '\040', '\154', '\145', '\040', '\061', '\012', + '\171', '\126', '\170', '\040', '\156', '\171', '\040', '\061', + '\012', '\163', '\120', '\146', '\040', '\163', '\164', '\040', + '\061', '\012', '\144', '\121', '\172', '\040', '\163', '\172', + '\040', '\061', '\012', '\163', '\132', '\142', '\040', '\163', + '\164', '\040', '\061', '\012', '\172', '\150', '\123', '\040', + '\164', '\150', '\040', '\061', '\012', '\153', '\127', '\142', + '\040', '\153', '\141', '\040', '\061', '\012', '\155', '\161', + '\120', '\040', '\161', '\165', '\040', '\061', '\012', '\106', + '\146', '\153', '\040', '\153', '\141', '\040', '\061', '\012', + '\170', '\161', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\161', '\110', '\040', '\161', '\165', '\040', + '\061', '\012', '\124', '\154', '\171', '\040', '\154', '\145', + '\040', '\061', '\012', '\153', '\160', '\114', '\040', '\153', + '\141', '\040', '\061', '\012', '\161', '\105', '\147', '\040', + '\161', '\165', '\040', '\061', '\012', '\142', '\115', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\170', '\122', + '\152', '\040', '\151', '\152', '\040', '\061', '\012', '\170', + '\163', '\103', '\040', '\163', '\164', '\040', '\061', '\012', + '\152', '\154', '\123', '\040', '\154', '\145', '\040', '\061', + '\012', '\154', '\172', '\115', '\040', '\154', '\145', '\040', + '\061', '\012', '\120', '\146', '\142', '\040', '\142', '\145', + '\040', '\061', '\012', '\165', '\112', '\166', '\040', '\161', + '\165', '\040', '\061', '\012', '\171', '\126', '\146', '\040', + '\156', '\171', '\040', '\061', '\012', '\132', '\147', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\170', '\142', + '\123', '\040', '\142', '\145', '\040', '\061', '\012', '\157', + '\106', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\170', '\166', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\143', '\125', '\040', '\164', '\150', '\040', + '\061', '\012', '\167', '\167', '\125', '\040', '\167', '\141', + '\040', '\061', '\012', '\171', '\103', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\155', '\120', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\163', '\112', '\144', + '\040', '\163', '\164', '\040', '\061', '\012', '\142', '\155', + '\116', '\040', '\155', '\145', '\040', '\061', '\012', '\165', + '\126', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\161', '\144', '\123', '\040', '\161', '\165', '\040', '\061', + '\012', '\126', '\167', '\160', '\040', '\160', '\162', '\040', + '\061', '\012', '\126', '\155', '\154', '\040', '\154', '\145', + '\040', '\061', '\012', '\110', '\161', '\171', '\040', '\161', + '\165', '\040', '\061', '\012', '\114', '\146', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\101', '\171', '\152', + '\040', '\151', '\152', '\040', '\061', '\012', '\171', '\170', + '\113', '\040', '\156', '\171', '\040', '\061', '\012', '\110', + '\167', '\166', '\040', '\166', '\141', '\040', '\061', '\012', + '\147', '\111', '\160', '\040', '\156', '\147', '\040', '\061', + '\012', '\132', '\147', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\130', '\164', '\167', '\040', '\164', '\150', + '\040', '\061', '\012', '\150', '\114', '\146', '\040', '\164', + '\150', '\040', '\061', '\012', '\116', '\153', '\144', '\040', + '\144', '\145', '\040', '\061', '\012', '\152', '\115', '\163', + '\040', '\163', '\164', '\040', '\061', '\012', '\170', '\106', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\170', + '\102', '\167', '\040', '\167', '\141', '\040', '\061', '\012', + '\167', '\110', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\121', '\172', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\147', '\131', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\120', '\166', '\153', '\040', '\153', + '\141', '\040', '\061', '\012', '\160', '\166', '\131', '\040', + '\166', '\141', '\040', '\061', '\012', '\112', '\170', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\165', '\147', + '\121', '\040', '\161', '\165', '\040', '\061', '\012', '\114', + '\161', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\170', '\154', '\114', '\040', '\154', '\145', '\040', '\061', + '\012', '\167', '\115', '\142', '\040', '\167', '\141', '\040', + '\061', '\012', '\123', '\142', '\172', '\040', '\163', '\172', + '\040', '\061', '\012', '\166', '\105', '\166', '\040', '\166', + '\141', '\040', '\061', '\012', '\161', '\146', '\172', '\040', + '\161', '\165', '\040', '\061', '\012', '\147', '\143', '\123', + '\040', '\143', '\150', '\040', '\061', '\012', '\164', '\103', + '\161', '\040', '\164', '\150', '\040', '\061', '\012', '\171', + '\110', '\160', '\040', '\160', '\162', '\040', '\061', '\012', + '\172', '\153', '\106', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\165', '\113', '\040', '\161', '\165', '\040', + '\061', '\012', '\124', '\142', '\146', '\040', '\142', '\145', + '\040', '\061', '\012', '\111', '\160', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\131', '\172', '\153', '\040', + '\163', '\172', '\040', '\061', '\012', '\121', '\167', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\160', '\106', + '\152', '\040', '\151', '\152', '\040', '\061', '\012', '\152', + '\120', '\155', '\040', '\151', '\152', '\040', '\061', '\012', + '\104', '\160', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\112', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\167', '\160', '\116', '\040', '\160', '\162', + '\040', '\061', '\012', '\167', '\172', '\105', '\040', '\163', + '\172', '\040', '\061', '\012', '\147', '\161', '\104', '\040', + '\161', '\165', '\040', '\061', '\012', '\130', '\167', '\155', + '\040', '\155', '\145', '\040', '\061', '\012', '\157', '\121', + '\170', '\040', '\157', '\156', '\040', '\061', '\012', '\154', + '\103', '\160', '\040', '\154', '\145', '\040', '\061', '\012', + '\115', '\150', '\153', '\040', '\164', '\150', '\040', '\061', + '\012', '\144', '\124', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\170', '\125', '\167', '\040', '\167', '\141', + '\040', '\061', '\012', '\150', '\147', '\105', '\040', '\164', + '\150', '\040', '\061', '\012', '\147', '\143', '\102', '\040', + '\143', '\150', '\040', '\061', '\012', '\150', '\160', '\112', + '\040', '\164', '\150', '\040', '\061', '\012', '\155', '\161', + '\113', '\040', '\161', '\165', '\040', '\061', '\012', '\147', + '\102', '\156', '\040', '\141', '\156', '\040', '\061', '\012', + '\150', '\111', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\161', '\104', '\040', '\161', '\165', '\040', + '\061', '\012', '\167', '\120', '\170', '\040', '\167', '\141', + '\040', '\061', '\012', '\163', '\115', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\171', '\130', '\167', '\040', + '\167', '\141', '\040', '\061', '\012', '\152', '\113', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\114', '\162', + '\172', '\040', '\145', '\162', '\040', '\061', '\012', '\110', + '\167', '\152', '\040', '\151', '\152', '\040', '\061', '\012', + '\171', '\146', '\127', '\040', '\156', '\171', '\040', '\061', + '\012', '\131', '\171', '\165', '\040', '\161', '\165', '\040', + '\061', '\012', '\161', '\131', '\163', '\040', '\161', '\165', + '\040', '\061', '\012', '\171', '\166', '\122', '\040', '\166', + '\141', '\040', '\061', '\012', '\163', '\122', '\172', '\040', + '\163', '\164', '\040', '\061', '\012', '\113', '\171', '\170', + '\040', '\156', '\171', '\040', '\061', '\012', '\156', '\170', + '\122', '\040', '\141', '\156', '\040', '\061', '\012', '\143', + '\144', '\112', '\040', '\143', '\150', '\040', '\061', '\012', + '\116', '\167', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\164', '\142', '\105', '\040', '\164', '\150', '\040', + '\061', '\012', '\157', '\145', '\132', '\040', '\145', '\162', + '\040', '\061', '\012', '\142', '\143', '\121', '\040', '\143', + '\150', '\040', '\061', '\012', '\123', '\167', '\142', '\040', + '\167', '\141', '\040', '\061', '\012', '\111', '\153', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\102', '\166', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\172', + '\150', '\106', '\040', '\164', '\150', '\040', '\061', '\012', + '\130', '\161', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\113', '\142', '\040', '\153', '\141', '\040', + '\061', '\012', '\127', '\144', '\153', '\040', '\144', '\145', + '\040', '\061', '\012', '\167', '\160', '\120', '\040', '\160', + '\162', '\040', '\061', '\012', '\153', '\121', '\171', '\040', + '\153', '\141', '\040', '\061', '\012', '\102', '\161', '\145', + '\040', '\161', '\165', '\040', '\061', '\012', '\161', '\146', + '\132', '\040', '\161', '\165', '\040', '\061', '\012', '\160', + '\120', '\167', '\040', '\160', '\162', '\040', '\061', '\012', + '\101', '\157', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\154', '\112', '\040', '\154', '\145', '\040', + '\061', '\012', '\131', '\156', '\166', '\040', '\141', '\156', + '\040', '\061', '\012', '\152', '\115', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\142', '\121', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\141', '\146', '\115', + '\040', '\141', '\156', '\040', '\061', '\012', '\152', '\166', + '\117', '\040', '\151', '\152', '\040', '\061', '\012', '\145', + '\110', '\146', '\040', '\145', '\162', '\040', '\061', '\012', + '\150', '\121', '\147', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\161', '\131', '\040', '\161', '\165', '\040', + '\061', '\012', '\172', '\112', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\160', '\131', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\161', '\145', '\115', '\040', + '\161', '\165', '\040', '\061', '\012', '\113', '\160', '\153', + '\040', '\153', '\141', '\040', '\061', '\012', '\153', '\146', + '\127', '\040', '\153', '\141', '\040', '\061', '\012', '\127', + '\144', '\163', '\040', '\163', '\164', '\040', '\061', '\012', + '\142', '\116', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\166', '\102', '\170', '\040', '\166', '\141', '\040', + '\061', '\012', '\163', '\165', '\112', '\040', '\161', '\165', + '\040', '\061', '\012', '\161', '\105', '\170', '\040', '\161', + '\165', '\040', '\061', '\012', '\162', '\146', '\132', '\040', + '\145', '\162', '\040', '\061', '\012', '\157', '\110', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\145', '\106', + '\167', '\040', '\145', '\162', '\040', '\061', '\012', '\146', + '\120', '\160', '\040', '\160', '\162', '\040', '\061', '\012', + '\153', '\104', '\142', '\040', '\153', '\141', '\040', '\061', + '\012', '\164', '\132', '\156', '\040', '\164', '\150', '\040', + '\061', '\012', '\144', '\143', '\113', '\040', '\143', '\150', + '\040', '\061', '\012', '\171', '\127', '\166', '\040', '\166', + '\141', '\040', '\061', '\012', '\125', '\170', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\171', '\121', '\145', + '\040', '\145', '\162', '\040', '\061', '\012', '\132', '\152', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\127', + '\152', '\166', '\040', '\151', '\152', '\040', '\061', '\012', + '\171', '\147', '\117', '\040', '\156', '\147', '\040', '\061', + '\012', '\157', '\152', '\121', '\040', '\157', '\156', '\040', + '\061', '\012', '\113', '\167', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\160', '\106', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\163', '\115', '\144', '\040', + '\163', '\164', '\040', '\061', '\012', '\115', '\146', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\115', '\172', + '\171', '\040', '\163', '\172', '\040', '\061', '\012', '\116', + '\167', '\160', '\040', '\160', '\162', '\040', '\061', '\012', + '\171', '\167', '\124', '\040', '\167', '\141', '\040', '\061', + '\012', '\167', '\114', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\110', '\161', '\155', '\040', '\161', '\165', + '\040', '\061', '\012', '\161', '\163', '\103', '\040', '\161', + '\165', '\040', '\061', '\012', '\142', '\116', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\142', '\125', '\166', + '\040', '\166', '\141', '\040', '\061', '\012', '\156', '\122', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\122', + '\154', '\153', '\040', '\154', '\145', '\040', '\061', '\012', + '\102', '\161', '\160', '\040', '\161', '\165', '\040', '\061', + '\012', '\143', '\146', '\111', '\040', '\143', '\150', '\040', + '\061', '\012', '\155', '\126', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\161', '\107', '\152', '\040', '\161', + '\165', '\040', '\061', '\012', '\166', '\154', '\130', '\040', + '\154', '\145', '\040', '\061', '\012', '\153', '\146', '\107', + '\040', '\153', '\141', '\040', '\061', '\012', '\167', '\126', + '\144', '\040', '\144', '\145', '\040', '\061', '\012', '\143', + '\144', '\105', '\040', '\143', '\150', '\040', '\061', '\012', + '\150', '\172', '\105', '\040', '\164', '\150', '\040', '\061', + '\012', '\104', '\150', '\166', '\040', '\164', '\150', '\040', + '\061', '\012', '\142', '\172', '\152', '\040', '\163', '\172', + '\040', '\061', '\012', '\166', '\166', '\114', '\040', '\166', + '\141', '\040', '\061', '\012', '\142', '\172', '\121', '\040', + '\163', '\172', '\040', '\061', '\012', '\167', '\126', '\142', + '\040', '\167', '\141', '\040', '\061', '\012', '\132', '\170', + '\154', '\040', '\154', '\145', '\040', '\061', '\012', '\172', + '\114', '\167', '\040', '\163', '\172', '\040', '\061', '\012', + '\150', '\124', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\126', '\161', '\160', '\040', '\161', '\165', '\040', + '\061', '\012', '\150', '\155', '\127', '\040', '\164', '\150', + '\040', '\061', '\012', '\146', '\154', '\104', '\040', '\154', + '\145', '\040', '\061', '\012', '\113', '\143', '\144', '\040', + '\143', '\150', '\040', '\061', '\012', '\160', '\104', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\153', '\166', + '\131', '\040', '\153', '\141', '\040', '\061', '\012', '\143', + '\121', '\154', '\040', '\143', '\150', '\040', '\061', '\012', + '\111', '\170', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\163', '\107', '\146', '\040', '\163', '\164', '\040', + '\061', '\012', '\147', '\106', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\122', '\153', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\161', '\110', '\154', '\040', + '\161', '\165', '\040', '\061', '\012', '\162', '\103', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\161', '\102', + '\156', '\040', '\141', '\156', '\040', '\061', '\012', '\163', + '\112', '\167', '\040', '\163', '\164', '\040', '\061', '\012', + '\143', '\127', '\152', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\130', '\160', '\040', '\163', '\172', '\040', + '\061', '\012', '\110', '\150', '\154', '\040', '\164', '\150', + '\040', '\061', '\012', '\150', '\152', '\120', '\040', '\164', + '\150', '\040', '\061', '\012', '\161', '\154', '\132', '\040', + '\161', '\165', '\040', '\061', '\012', '\110', '\170', '\162', + '\040', '\145', '\162', '\040', '\061', '\012', '\172', '\162', + '\105', '\040', '\145', '\162', '\040', '\061', '\012', '\147', + '\153', '\110', '\040', '\156', '\147', '\040', '\061', '\012', + '\165', '\110', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\107', '\172', '\155', '\040', '\163', '\172', '\040', + '\061', '\012', '\143', '\102', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\172', '\146', '\146', '\040', '\163', + '\172', '\040', '\061', '\012', '\172', '\114', '\163', '\040', + '\163', '\164', '\040', '\061', '\012', '\125', '\161', '\171', + '\040', '\161', '\165', '\040', '\061', '\012', '\166', '\153', + '\104', '\040', '\153', '\141', '\040', '\061', '\012', '\146', + '\161', '\130', '\040', '\161', '\165', '\040', '\061', '\012', + '\150', '\114', '\152', '\040', '\164', '\150', '\040', '\061', + '\012', '\146', '\131', '\165', '\040', '\161', '\165', '\040', + '\061', '\012', '\152', '\113', '\167', '\040', '\151', '\152', + '\040', '\061', '\012', '\152', '\111', '\142', '\040', '\151', + '\152', '\040', '\061', '\012', '\156', '\162', '\125', '\040', + '\141', '\156', '\040', '\061', '\012', '\146', '\106', '\160', + '\040', '\160', '\162', '\040', '\061', '\012', '\163', '\142', + '\103', '\040', '\163', '\164', '\040', '\061', '\012', '\155', + '\107', '\166', '\040', '\166', '\141', '\040', '\061', '\012', + '\146', '\130', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\120', '\153', '\166', '\040', '\153', '\141', '\040', + '\061', '\012', '\103', '\161', '\145', '\040', '\161', '\165', + '\040', '\061', '\012', '\143', '\103', '\170', '\040', '\143', + '\150', '\040', '\061', '\012', '\162', '\116', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\132', '\167', '\146', + '\040', '\167', '\141', '\040', '\061', '\012', '\112', '\147', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\170', + '\154', '\121', '\040', '\154', '\145', '\040', '\061', '\012', + '\147', '\102', '\172', '\040', '\156', '\147', '\040', '\061', + '\012', '\143', '\111', '\170', '\040', '\143', '\150', '\040', + '\061', '\012', '\157', '\144', '\121', '\040', '\157', '\156', + '\040', '\061', '\012', '\121', '\156', '\172', '\040', '\141', + '\156', '\040', '\061', '\012', '\125', '\172', '\170', '\040', + '\163', '\172', '\040', '\061', '\012', '\112', '\160', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\147', '\170', + '\130', '\040', '\156', '\147', '\040', '\061', '\012', '\132', + '\153', '\144', '\040', '\144', '\145', '\040', '\061', '\012', + '\130', '\153', '\153', '\040', '\153', '\141', '\040', '\061', + '\012', '\150', '\122', '\166', '\040', '\164', '\150', '\040', + '\061', '\012', '\171', '\143', '\126', '\040', '\143', '\150', + '\040', '\061', '\012', '\172', '\115', '\155', '\040', '\163', + '\172', '\040', '\061', '\012', '\145', '\102', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\147', '\110', '\144', + '\040', '\156', '\147', '\040', '\061', '\012', '\142', '\170', + '\125', '\040', '\142', '\145', '\040', '\061', '\012', '\170', + '\144', '\113', '\040', '\144', '\145', '\040', '\061', '\012', + '\155', '\121', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\164', '\131', '\152', '\040', '\164', '\150', '\040', + '\061', '\012', '\150', '\154', '\106', '\040', '\164', '\150', + '\040', '\061', '\012', '\143', '\122', '\172', '\040', '\143', + '\150', '\040', '\061', '\012', '\154', '\107', '\172', '\040', + '\154', '\145', '\040', '\061', '\012', '\172', '\106', '\172', + '\040', '\172', '\145', '\040', '\061', '\012', '\161', '\117', + '\160', '\040', '\161', '\165', '\040', '\061', '\012', '\107', + '\147', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\157', '\107', '\155', '\040', '\157', '\156', '\040', '\061', + '\012', '\130', '\156', '\160', '\040', '\141', '\156', '\040', + '\061', '\012', '\167', '\131', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\167', '\165', '\112', '\040', '\161', + '\165', '\040', '\061', '\012', '\163', '\116', '\163', '\040', + '\163', '\164', '\040', '\061', '\012', '\172', '\161', '\125', + '\040', '\161', '\165', '\040', '\061', '\012', '\153', '\103', + '\160', '\040', '\153', '\141', '\040', '\061', '\012', '\127', + '\150', '\167', '\040', '\164', '\150', '\040', '\061', '\012', + '\156', '\121', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\167', '\101', '\040', '\166', '\141', '\040', + '\061', '\012', '\126', '\143', '\147', '\040', '\143', '\150', + '\040', '\061', '\012', '\153', '\127', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\110', '\161', '\144', '\040', + '\161', '\165', '\040', '\061', '\012', '\103', '\160', '\171', + '\040', '\160', '\162', '\040', '\061', '\012', '\172', '\143', + '\114', '\040', '\143', '\150', '\040', '\061', '\012', '\143', + '\146', '\106', '\040', '\143', '\150', '\040', '\061', '\012', + '\153', '\130', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\141', '\130', '\152', '\040', '\141', '\156', '\040', + '\061', '\012', '\123', '\167', '\153', '\040', '\153', '\141', + '\040', '\061', '\012', '\146', '\150', '\161', '\040', '\164', + '\150', '\040', '\061', '\012', '\126', '\170', '\151', '\040', + '\151', '\156', '\040', '\061', '\012', '\107', '\161', '\165', + '\040', '\165', '\156', '\040', '\061', '\012', '\125', '\170', + '\144', '\040', '\144', '\145', '\040', '\061', '\012', '\172', + '\144', '\113', '\040', '\163', '\172', '\040', '\061', '\012', + '\150', '\132', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\167', '\112', '\040', '\155', '\145', '\040', + '\061', '\012', '\143', '\166', '\104', '\040', '\143', '\150', + '\040', '\061', '\012', '\154', '\142', '\132', '\040', '\154', + '\145', '\040', '\061', '\012', '\120', '\172', '\154', '\040', + '\154', '\145', '\040', '\061', '\012', '\150', '\144', '\117', + '\040', '\164', '\150', '\040', '\061', '\012', '\150', '\112', + '\156', '\040', '\164', '\150', '\040', '\061', '\012', '\161', + '\127', '\160', '\040', '\161', '\165', '\040', '\061', '\012', + '\144', '\130', '\171', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\165', '\125', '\040', '\161', '\165', '\040', + '\061', '\012', '\146', '\130', '\171', '\040', '\156', '\171', + '\040', '\061', '\012', '\170', '\156', '\114', '\040', '\141', + '\156', '\040', '\061', '\012', '\147', '\115', '\146', '\040', + '\156', '\147', '\040', '\061', '\012', '\162', '\116', '\146', + '\040', '\145', '\162', '\040', '\061', '\012', '\170', '\121', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\153', + '\161', '\110', '\040', '\161', '\165', '\040', '\061', '\012', + '\162', '\106', '\172', '\040', '\145', '\162', '\040', '\061', + '\012', '\166', '\160', '\124', '\040', '\166', '\141', '\040', + '\061', '\012', '\116', '\167', '\171', '\040', '\167', '\141', + '\040', '\061', '\012', '\171', '\161', '\101', '\040', '\161', + '\165', '\040', '\061', '\012', '\166', '\150', '\117', '\040', + '\164', '\150', '\040', '\061', '\012', '\153', '\126', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\156', '\131', + '\142', '\040', '\141', '\156', '\040', '\061', '\012', '\152', + '\166', '\116', '\040', '\151', '\152', '\040', '\061', '\012', + '\142', '\111', '\146', '\040', '\142', '\145', '\040', '\061', + '\012', '\161', '\161', '\123', '\040', '\161', '\165', '\040', + '\061', '\012', '\152', '\142', '\106', '\040', '\151', '\152', + '\040', '\061', '\012', '\147', '\115', '\153', '\040', '\156', + '\147', '\040', '\061', '\012', '\142', '\124', '\144', '\040', + '\144', '\145', '\040', '\061', '\012', '\122', '\150', '\144', + '\040', '\164', '\150', '\040', '\061', '\012', '\164', '\127', + '\161', '\040', '\164', '\150', '\040', '\061', '\012', '\147', + '\114', '\172', '\040', '\156', '\147', '\040', '\061', '\012', + '\146', '\163', '\104', '\040', '\163', '\164', '\040', '\061', + '\012', '\165', '\115', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\171', '\110', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\130', '\147', '\152', '\040', '\156', + '\147', '\040', '\061', '\012', '\114', '\155', '\155', '\040', + '\155', '\145', '\040', '\061', '\012', '\166', '\153', '\125', + '\040', '\153', '\141', '\040', '\061', '\012', '\154', '\101', + '\170', '\040', '\154', '\145', '\040', '\061', '\012', '\113', + '\172', '\144', '\040', '\163', '\172', '\040', '\061', '\012', + '\150', '\113', '\155', '\040', '\164', '\150', '\040', '\061', + '\012', '\153', '\121', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\147', '\106', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\167', '\171', '\130', '\040', '\167', + '\141', '\040', '\061', '\012', '\172', '\146', '\125', '\040', + '\163', '\172', '\040', '\061', '\012', '\170', '\160', '\125', + '\040', '\160', '\162', '\040', '\061', '\012', '\171', '\167', + '\112', '\040', '\167', '\141', '\040', '\061', '\012', '\101', + '\171', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\147', '\111', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\165', '\121', '\040', '\161', '\165', '\040', + '\061', '\012', '\126', '\146', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\166', '\102', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\110', '\164', '\171', '\040', + '\164', '\150', '\040', '\061', '\012', '\147', '\122', '\166', + '\040', '\156', '\147', '\040', '\061', '\012', '\160', '\124', + '\142', '\040', '\160', '\162', '\040', '\061', '\012', '\125', + '\161', '\170', '\040', '\161', '\165', '\040', '\061', '\012', + '\166', '\124', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\112', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\125', '\151', '\167', '\040', '\151', '\156', + '\040', '\061', '\012', '\112', '\154', '\160', '\040', '\154', + '\145', '\040', '\061', '\012', '\172', '\120', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\162', '\103', '\170', + '\040', '\145', '\162', '\040', '\061', '\012', '\154', '\161', + '\123', '\040', '\161', '\165', '\040', '\061', '\012', '\172', + '\154', '\132', '\040', '\154', '\145', '\040', '\061', '\012', + '\172', '\117', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\153', '\154', '\113', '\040', '\154', '\145', '\040', + '\061', '\012', '\153', '\146', '\121', '\040', '\153', '\141', + '\040', '\061', '\012', '\165', '\112', '\170', '\040', '\161', + '\165', '\040', '\061', '\012', '\160', '\153', '\120', '\040', + '\153', '\141', '\040', '\061', '\012', '\107', '\161', '\172', + '\040', '\161', '\165', '\040', '\061', '\012', '\112', '\154', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\171', + '\171', '\104', '\040', '\156', '\171', '\040', '\061', '\012', + '\152', '\150', '\130', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\162', '\126', '\040', '\143', '\150', '\040', + '\061', '\012', '\104', '\167', '\167', '\040', '\167', '\141', + '\040', '\061', '\012', '\171', '\152', '\167', '\040', '\151', + '\152', '\040', '\061', '\012', '\161', '\160', '\130', '\040', + '\161', '\165', '\040', '\061', '\012', '\121', '\155', '\144', + '\040', '\144', '\145', '\040', '\061', '\012', '\171', '\127', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\167', + '\120', '\144', '\040', '\144', '\145', '\040', '\061', '\012', + '\125', '\161', '\153', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\142', '\122', '\040', '\141', '\156', '\040', + '\061', '\012', '\131', '\144', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\161', '\121', '\154', '\040', '\161', + '\165', '\040', '\061', '\012', '\160', '\155', '\104', '\040', + '\155', '\145', '\040', '\061', '\012', '\112', '\153', '\152', + '\040', '\153', '\141', '\040', '\061', '\012', '\152', '\124', + '\153', '\040', '\153', '\141', '\040', '\061', '\012', '\167', + '\131', '\146', '\040', '\167', '\141', '\040', '\061', '\012', + '\132', '\172', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\162', '\153', '\121', '\040', '\145', '\162', '\040', + '\061', '\012', '\142', '\104', '\160', '\040', '\160', '\162', + '\040', '\061', '\012', '\161', '\123', '\163', '\040', '\161', + '\165', '\040', '\061', '\012', '\147', '\130', '\162', '\040', + '\156', '\147', '\040', '\061', '\012', '\143', '\132', '\142', + '\040', '\143', '\150', '\040', '\061', '\012', '\116', '\147', + '\160', '\040', '\156', '\147', '\040', '\061', '\012', '\150', + '\161', '\121', '\040', '\164', '\150', '\040', '\061', '\012', + '\127', '\166', '\167', '\040', '\166', '\141', '\040', '\061', + '\012', '\127', '\142', '\167', '\040', '\167', '\141', '\040', + '\061', '\012', '\167', '\166', '\113', '\040', '\166', '\141', + '\040', '\061', '\012', '\143', '\112', '\146', '\040', '\143', + '\150', '\040', '\061', '\012', '\115', '\167', '\144', '\040', + '\144', '\145', '\040', '\061', '\012', '\144', '\144', '\112', + '\040', '\144', '\145', '\040', '\061', '\012', '\151', '\167', + '\105', '\040', '\151', '\156', '\040', '\061', '\012', '\142', + '\170', '\130', '\040', '\142', '\145', '\040', '\061', '\012', + '\152', '\170', '\124', '\040', '\151', '\152', '\040', '\061', + '\012', '\131', '\143', '\156', '\040', '\143', '\150', '\040', + '\061', '\012', '\167', '\115', '\146', '\040', '\167', '\141', + '\040', '\061', '\012', '\142', '\161', '\104', '\040', '\161', + '\165', '\040', '\061', '\012', '\171', '\161', '\111', '\040', + '\161', '\165', '\040', '\061', '\012', '\144', '\122', '\152', + '\040', '\144', '\145', '\040', '\061', '\012', '\167', '\131', + '\171', '\040', '\167', '\141', '\040', '\061', '\012', '\124', + '\170', '\172', '\040', '\163', '\172', '\040', '\061', '\012', + '\166', '\162', '\116', '\040', '\145', '\162', '\040', '\061', + '\012', '\161', '\126', '\165', '\040', '\165', '\156', '\040', + '\061', '\012', '\155', '\122', '\152', '\040', '\151', '\152', + '\040', '\061', '\012', '\106', '\152', '\170', '\040', '\151', + '\152', '\040', '\061', '\012', '\146', '\171', '\121', '\040', + '\156', '\171', '\040', '\061', '\012', '\170', '\145', '\111', + '\040', '\145', '\162', '\040', '\061', '\012', '\127', '\161', + '\146', '\040', '\161', '\165', '\040', '\061', '\012', '\112', + '\154', '\171', '\040', '\154', '\145', '\040', '\061', '\012', + '\152', '\104', '\142', '\040', '\151', '\152', '\040', '\061', + '\012', '\131', '\172', '\165', '\040', '\161', '\165', '\040', + '\061', '\012', '\102', '\170', '\155', '\040', '\155', '\145', + '\040', '\061', '\012', '\167', '\114', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\142', '\161', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\163', '\147', '\113', + '\040', '\156', '\147', '\040', '\061', '\012', '\153', '\161', + '\127', '\040', '\161', '\165', '\040', '\061', '\012', '\132', + '\163', '\156', '\040', '\141', '\156', '\040', '\061', '\012', + '\106', '\161', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\130', '\172', '\040', '\145', '\162', '\040', + '\061', '\012', '\154', '\112', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\152', '\105', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\156', '\103', '\142', '\040', + '\141', '\156', '\040', '\061', '\012', '\130', '\162', '\144', + '\040', '\145', '\162', '\040', '\061', '\012', '\122', '\172', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\147', + '\146', '\127', '\040', '\156', '\147', '\040', '\061', '\012', + '\130', '\164', '\154', '\040', '\164', '\150', '\040', '\061', + '\012', '\155', '\124', '\170', '\040', '\155', '\145', '\040', + '\061', '\012', '\165', '\146', '\101', '\040', '\161', '\165', + '\040', '\061', '\012', '\167', '\152', '\121', '\040', '\151', + '\152', '\040', '\061', '\012', '\170', '\154', '\127', '\040', + '\154', '\145', '\040', '\061', '\012', '\144', '\161', '\110', + '\040', '\161', '\165', '\040', '\061', '\012', '\170', '\150', + '\115', '\040', '\164', '\150', '\040', '\061', '\012', '\130', + '\167', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\144', '\156', '\127', '\040', '\141', '\156', '\040', '\061', + '\012', '\122', '\146', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\146', '\113', '\160', '\040', '\160', '\162', + '\040', '\061', '\012', '\153', '\106', '\167', '\040', '\153', + '\141', '\040', '\061', '\012', '\121', '\165', '\166', '\040', + '\161', '\165', '\040', '\061', '\012', '\155', '\130', '\167', + '\040', '\155', '\145', '\040', '\061', '\012', '\126', '\153', + '\167', '\040', '\153', '\141', '\040', '\061', '\012', '\164', + '\106', '\150', '\040', '\143', '\150', '\040', '\061', '\012', + '\150', '\111', '\165', '\040', '\164', '\150', '\040', '\061', + '\012', '\154', '\124', '\146', '\040', '\154', '\145', '\040', + '\061', '\012', '\115', '\167', '\166', '\040', '\166', '\141', + '\040', '\061', '\012', '\167', '\166', '\124', '\040', '\166', + '\141', '\040', '\061', '\012', '\153', '\113', '\160', '\040', + '\153', '\141', '\040', '\061', '\012', '\164', '\122', '\166', + '\040', '\164', '\150', '\040', '\061', '\012', '\167', '\130', + '\157', '\040', '\157', '\156', '\040', '\061', '\012', '\166', + '\172', '\114', '\040', '\163', '\172', '\040', '\061', '\012', + '\112', '\143', '\146', '\040', '\143', '\150', '\040', '\061', + '\012', '\124', '\142', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\152', '\144', '\121', '\040', '\144', '\145', + '\040', '\061', '\012', '\122', '\142', '\170', '\040', '\142', + '\145', '\040', '\061', '\012', '\112', '\162', '\155', '\040', + '\145', '\162', '\040', '\061', '\012', '\163', '\122', '\152', + '\040', '\163', '\164', '\040', '\061', '\012', '\172', '\127', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\161', + '\156', '\105', '\040', '\141', '\156', '\040', '\061', '\012', + '\113', '\143', '\146', '\040', '\143', '\150', '\040', '\061', + '\012', '\121', '\161', '\155', '\040', '\161', '\165', '\040', + '\061', '\012', '\146', '\160', '\111', '\040', '\160', '\162', + '\040', '\061', '\012', '\151', '\116', '\167', '\040', '\151', + '\156', '\040', '\061', '\012', '\165', '\152', '\105', '\040', + '\161', '\165', '\040', '\061', '\012', '\161', '\110', '\166', + '\040', '\161', '\165', '\040', '\061', '\012', '\112', '\166', + '\170', '\040', '\166', '\141', '\040', '\061', '\012', '\150', + '\110', '\143', '\040', '\164', '\150', '\040', '\061', '\012', + '\146', '\166', '\112', '\040', '\166', '\141', '\040', '\061', + '\012', '\156', '\161', '\131', '\040', '\141', '\156', '\040', + '\061', '\012', '\167', '\160', '\105', '\040', '\167', '\141', + '\040', '\061', '\012', '\110', '\167', '\163', '\040', '\163', + '\164', '\040', '\061', '\012', '\170', '\172', '\111', '\040', + '\163', '\172', '\040', '\061', '\012', '\103', '\147', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\143', '\127', + '\144', '\040', '\143', '\150', '\040', '\061', '\012', '\161', + '\165', '\126', '\040', '\165', '\156', '\040', '\061', '\012', + '\142', '\152', '\116', '\040', '\151', '\152', '\040', '\061', + '\012', '\170', '\121', '\160', '\040', '\160', '\162', '\040', + '\061', '\012', '\142', '\170', '\105', '\040', '\142', '\145', + '\040', '\061', '\012', '\165', '\126', '\153', '\040', '\161', + '\165', '\040', '\061', '\012', '\127', '\162', '\154', '\040', + '\145', '\162', '\040', '\061', '\012', '\114', '\162', '\170', + '\040', '\145', '\162', '\040', '\061', '\012', '\111', '\167', + '\154', '\040', '\154', '\145', '\040', '\061', '\012', '\141', + '\161', '\102', '\040', '\141', '\156', '\040', '\061', '\012', + '\126', '\143', '\160', '\040', '\143', '\150', '\040', '\061', + '\012', '\127', '\167', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\141', '\107', '\170', '\040', '\141', '\156', + '\040', '\061', '\012', '\146', '\120', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\155', '\106', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\161', '\147', '\144', + '\040', '\161', '\165', '\040', '\061', '\012', '\132', '\163', + '\144', '\040', '\163', '\164', '\040', '\061', '\012', '\126', + '\170', '\163', '\040', '\163', '\172', '\040', '\061', '\012', + '\113', '\150', '\161', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\123', '\163', '\040', '\163', '\164', '\040', + '\061', '\012', '\157', '\107', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\131', '\172', '\166', '\040', '\163', + '\172', '\040', '\061', '\012', '\144', '\161', '\130', '\040', + '\161', '\165', '\040', '\061', '\012', '\155', '\160', '\121', + '\040', '\155', '\145', '\040', '\061', '\012', '\113', '\143', + '\160', '\040', '\143', '\150', '\040', '\061', '\012', '\163', + '\167', '\104', '\040', '\163', '\164', '\040', '\061', '\012', + '\162', '\132', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\131', '\155', '\040', '\151', '\152', '\040', + '\061', '\012', '\165', '\112', '\154', '\040', '\161', '\165', + '\040', '\061', '\012', '\166', '\127', '\166', '\040', '\166', + '\141', '\040', '\061', '\012', '\163', '\166', '\117', '\040', + '\163', '\164', '\040', '\061', '\012', '\160', '\106', '\144', + '\040', '\144', '\145', '\040', '\061', '\012', '\131', '\152', + '\170', '\040', '\151', '\152', '\040', '\061', '\012', '\164', + '\160', '\111', '\040', '\164', '\150', '\040', '\061', '\012', + '\144', '\126', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\116', '\155', '\040', '\163', '\164', '\040', + '\061', '\012', '\154', '\113', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\156', '\166', '\125', '\040', '\141', + '\156', '\040', '\061', '\012', '\110', '\170', '\146', '\040', + '\146', '\157', '\040', '\061', '\012', '\160', '\165', '\127', + '\040', '\161', '\165', '\040', '\061', '\012', '\167', '\112', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\147', + '\170', '\122', '\040', '\156', '\147', '\040', '\061', '\012', + '\146', '\101', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\131', '\161', '\145', '\040', '\161', '\165', '\040', + '\061', '\012', '\120', '\167', '\172', '\040', '\163', '\172', + '\040', '\061', '\012', '\150', '\155', '\103', '\040', '\164', + '\150', '\040', '\061', '\012', '\171', '\154', '\112', '\040', + '\154', '\145', '\040', '\061', '\012', '\155', '\161', '\124', + '\040', '\161', '\165', '\040', '\061', '\012', '\143', '\103', + '\146', '\040', '\143', '\150', '\040', '\061', '\012', '\160', + '\132', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\141', '\106', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\157', '\131', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\146', '\120', '\152', '\040', '\151', '\152', + '\040', '\061', '\012', '\144', '\112', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\170', '\167', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\103', '\143', '\142', + '\040', '\143', '\150', '\040', '\061', '\012', '\167', '\106', + '\156', '\040', '\141', '\156', '\040', '\061', '\012', '\167', + '\162', '\131', '\040', '\145', '\162', '\040', '\061', '\012', + '\103', '\144', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\150', '\114', '\143', '\040', '\164', '\150', '\040', + '\061', '\012', '\132', '\170', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\115', '\170', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\150', '\143', '\131', '\040', + '\164', '\150', '\040', '\061', '\012', '\172', '\126', '\167', + '\040', '\163', '\172', '\040', '\061', '\012', '\150', '\153', + '\126', '\040', '\164', '\150', '\040', '\061', '\012', '\164', + '\170', '\105', '\040', '\164', '\150', '\040', '\061', '\012', + '\171', '\166', '\124', '\040', '\166', '\141', '\040', '\061', + '\012', '\115', '\154', '\167', '\040', '\154', '\145', '\040', + '\061', '\012', '\172', '\164', '\106', '\040', '\164', '\150', + '\040', '\061', '\012', '\146', '\107', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\172', '\152', '\105', '\040', + '\163', '\172', '\040', '\061', '\012', '\147', '\152', '\115', + '\040', '\156', '\147', '\040', '\061', '\012', '\152', '\167', + '\120', '\040', '\151', '\152', '\040', '\061', '\012', '\113', + '\170', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\171', '\106', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\127', '\143', '\147', '\040', '\143', '\150', '\040', + '\061', '\012', '\164', '\150', '\132', '\040', '\143', '\150', + '\040', '\061', '\012', '\150', '\172', '\121', '\040', '\164', + '\150', '\040', '\061', '\012', '\112', '\164', '\147', '\040', + '\164', '\150', '\040', '\061', '\012', '\171', '\166', '\113', + '\040', '\166', '\141', '\040', '\061', '\012', '\172', '\126', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\120', + '\167', '\142', '\040', '\167', '\141', '\040', '\061', '\012', + '\170', '\161', '\104', '\040', '\161', '\165', '\040', '\061', + '\012', '\165', '\171', '\121', '\040', '\161', '\165', '\040', + '\061', '\012', '\147', '\103', '\155', '\040', '\156', '\147', + '\040', '\061', '\012', '\172', '\152', '\125', '\040', '\163', + '\172', '\040', '\061', '\012', '\170', '\107', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\115', '\161', '\171', + '\040', '\161', '\165', '\040', '\061', '\012', '\117', '\143', + '\170', '\040', '\143', '\150', '\040', '\061', '\012', '\163', + '\161', '\115', '\040', '\161', '\165', '\040', '\061', '\012', + '\154', '\122', '\142', '\040', '\154', '\145', '\040', '\061', + '\012', '\164', '\146', '\125', '\040', '\164', '\150', '\040', + '\061', '\012', '\166', '\132', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\146', '\132', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\147', '\160', '\132', '\040', + '\156', '\147', '\040', '\061', '\012', '\106', '\160', '\146', + '\040', '\160', '\162', '\040', '\061', '\012', '\161', '\164', + '\121', '\040', '\164', '\150', '\040', '\061', '\012', '\155', + '\150', '\132', '\040', '\164', '\150', '\040', '\061', '\012', + '\142', '\161', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\146', '\147', '\107', '\040', '\156', '\147', '\040', + '\061', '\012', '\167', '\157', '\124', '\040', '\157', '\156', + '\040', '\061', '\012', '\172', '\123', '\142', '\040', '\163', + '\172', '\040', '\061', '\012', '\167', '\170', '\123', '\040', + '\167', '\141', '\040', '\061', '\012', '\127', '\162', '\146', + '\040', '\145', '\162', '\040', '\061', '\012', '\117', '\161', + '\153', '\040', '\161', '\165', '\040', '\061', '\012', '\170', + '\114', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\121', '\172', '\152', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\130', '\153', '\040', '\153', '\141', '\040', + '\061', '\012', '\164', '\144', '\130', '\040', '\164', '\150', + '\040', '\061', '\012', '\112', '\161', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\146', '\130', '\153', '\040', + '\153', '\141', '\040', '\061', '\012', '\153', '\102', '\144', + '\040', '\144', '\145', '\040', '\061', '\012', '\151', '\161', + '\127', '\040', '\161', '\165', '\040', '\061', '\012', '\117', + '\143', '\142', '\040', '\143', '\150', '\040', '\061', '\012', + '\146', '\125', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\152', '\130', '\153', '\040', '\151', '\152', '\040', + '\061', '\012', '\150', '\142', '\111', '\040', '\164', '\150', + '\040', '\061', '\012', '\132', '\143', '\147', '\040', '\143', + '\150', '\040', '\061', '\012', '\172', '\167', '\123', '\040', + '\167', '\141', '\040', '\061', '\012', '\143', '\126', '\155', + '\040', '\143', '\150', '\040', '\061', '\012', '\166', '\167', + '\152', '\040', '\151', '\152', '\040', '\061', '\012', '\147', + '\167', '\107', '\040', '\156', '\147', '\040', '\061', '\012', + '\172', '\163', '\115', '\040', '\163', '\164', '\040', '\061', + '\012', '\120', '\161', '\157', '\040', '\161', '\165', '\040', + '\061', '\012', '\150', '\120', '\152', '\040', '\164', '\150', + '\040', '\061', '\012', '\146', '\167', '\107', '\040', '\167', + '\141', '\040', '\061', '\012', '\130', '\167', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\127', '\167', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\126', '\161', + '\167', '\040', '\161', '\165', '\040', '\061', '\012', '\166', + '\155', '\131', '\040', '\166', '\141', '\040', '\061', '\012', + '\165', '\166', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\146', '\113', '\040', '\164', '\150', '\040', + '\061', '\012', '\130', '\142', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\116', '\146', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\167', '\160', '\110', '\040', + '\160', '\162', '\040', '\061', '\012', '\171', '\112', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\167', '\161', + '\117', '\040', '\161', '\165', '\040', '\061', '\012', '\156', + '\143', '\126', '\040', '\143', '\150', '\040', '\061', '\012', + '\167', '\147', '\115', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\121', '\153', '\040', '\153', '\141', '\040', + '\061', '\012', '\150', '\166', '\113', '\040', '\164', '\150', + '\040', '\061', '\012', '\161', '\114', '\162', '\040', '\161', + '\165', '\040', '\061', '\012', '\127', '\143', '\145', '\040', + '\143', '\150', '\040', '\061', '\012', '\153', '\106', '\156', + '\040', '\141', '\156', '\040', '\061', '\012', '\162', '\102', + '\155', '\040', '\145', '\162', '\040', '\061', '\012', '\155', + '\144', '\126', '\040', '\144', '\145', '\040', '\061', '\012', + '\152', '\106', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\156', '\130', '\040', '\141', '\156', '\040', + '\061', '\012', '\156', '\115', '\146', '\040', '\141', '\156', + '\040', '\061', '\012', '\163', '\103', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\160', '\103', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\165', '\112', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\103', '\146', + '\153', '\040', '\153', '\141', '\040', '\061', '\012', '\103', + '\170', '\142', '\040', '\142', '\145', '\040', '\061', '\012', + '\146', '\117', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\141', '\112', '\172', '\040', '\141', '\156', '\040', + '\061', '\012', '\147', '\114', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\142', '\155', '\130', '\040', '\155', + '\145', '\040', '\061', '\012', '\131', '\146', '\157', '\040', + '\157', '\156', '\040', '\061', '\012', '\144', '\112', '\146', + '\040', '\144', '\145', '\040', '\061', '\012', '\105', '\141', + '\171', '\040', '\141', '\156', '\040', '\061', '\012', '\161', + '\123', '\144', '\040', '\161', '\165', '\040', '\061', '\012', + '\155', '\152', '\121', '\040', '\151', '\152', '\040', '\061', + '\012', '\160', '\116', '\153', '\040', '\153', '\141', '\040', + '\061', '\012', '\116', '\166', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\170', '\153', '\130', '\040', '\153', + '\141', '\040', '\061', '\012', '\112', '\167', '\170', '\040', + '\167', '\141', '\040', '\061', '\012', '\152', '\166', '\114', + '\040', '\151', '\152', '\040', '\061', '\012', '\146', '\160', + '\110', '\040', '\160', '\162', '\040', '\061', '\012', '\160', + '\170', '\117', '\040', '\160', '\162', '\040', '\061', '\012', + '\166', '\120', '\170', '\040', '\166', '\141', '\040', '\061', + '\012', '\144', '\127', '\165', '\040', '\161', '\165', '\040', + '\061', '\012', '\150', '\142', '\122', '\040', '\164', '\150', + '\040', '\061', '\012', '\167', '\157', '\105', '\040', '\157', + '\156', '\040', '\061', '\012', '\147', '\164', '\130', '\040', + '\164', '\150', '\040', '\061', '\012', '\142', '\146', '\106', + '\040', '\142', '\145', '\040', '\061', '\012', '\155', '\166', + '\127', '\040', '\166', '\141', '\040', '\061', '\012', '\170', + '\163', '\115', '\040', '\163', '\164', '\040', '\061', '\012', + '\167', '\114', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\167', '\110', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\163', '\103', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\160', '\114', '\167', '\040', '\160', + '\162', '\040', '\061', '\012', '\153', '\130', '\167', '\040', + '\153', '\141', '\040', '\061', '\012', '\170', '\126', '\154', + '\040', '\154', '\145', '\040', '\061', '\012', '\150', '\103', + '\143', '\040', '\164', '\150', '\040', '\061', '\012', '\157', + '\125', '\153', '\040', '\157', '\156', '\040', '\061', '\012', + '\172', '\143', '\106', '\040', '\143', '\150', '\040', '\061', + '\012', '\163', '\115', '\166', '\040', '\163', '\164', '\040', + '\061', '\012', '\144', '\162', '\132', '\040', '\145', '\162', + '\040', '\061', '\012', '\167', '\146', '\117', '\040', '\167', + '\141', '\040', '\061', '\012', '\171', '\106', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\150', '\130', '\141', + '\040', '\164', '\150', '\040', '\061', '\012', '\161', '\115', + '\165', '\040', '\165', '\156', '\040', '\061', '\012', '\146', + '\103', '\166', '\040', '\166', '\141', '\040', '\061', '\012', + '\146', '\167', '\103', '\040', '\167', '\141', '\040', '\061', + '\012', '\157', '\124', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\106', '\153', '\155', '\040', '\153', '\141', + '\040', '\061', '\012', '\145', '\121', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\120', '\170', '\144', '\040', + '\144', '\145', '\040', '\061', '\012', '\153', '\152', '\107', + '\040', '\151', '\152', '\040', '\061', '\012', '\164', '\107', + '\163', '\040', '\164', '\150', '\040', '\061', '\012', '\144', + '\161', '\102', '\040', '\161', '\165', '\040', '\061', '\012', + '\146', '\155', '\130', '\040', '\155', '\145', '\040', '\061', + '\012', '\170', '\131', '\151', '\040', '\151', '\156', '\040', + '\061', '\012', '\153', '\111', '\153', '\040', '\153', '\141', + '\040', '\061', '\012', '\166', '\104', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\153', '\166', '\103', '\040', + '\153', '\141', '\040', '\061', '\012', '\161', '\164', '\132', + '\040', '\164', '\150', '\040', '\061', '\012', '\146', '\120', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\144', + '\160', '\116', '\040', '\144', '\145', '\040', '\061', '\012', + '\150', '\116', '\162', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\156', '\152', '\040', '\141', '\156', '\040', + '\061', '\012', '\110', '\153', '\145', '\040', '\145', '\162', + '\040', '\061', '\012', '\111', '\161', '\160', '\040', '\161', + '\165', '\040', '\061', '\012', '\167', '\146', '\116', '\040', + '\167', '\141', '\040', '\061', '\012', '\126', '\150', '\170', + '\040', '\164', '\150', '\040', '\061', '\012', '\104', '\147', + '\153', '\040', '\156', '\147', '\040', '\061', '\012', '\155', + '\153', '\121', '\040', '\153', '\141', '\040', '\061', '\012', + '\127', '\170', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\111', '\143', '\170', '\040', '\143', '\150', '\040', + '\061', '\012', '\171', '\131', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\164', '\161', '\170', '\040', '\164', + '\150', '\040', '\061', '\012', '\132', '\166', '\146', '\040', + '\166', '\141', '\040', '\061', '\012', '\163', '\170', '\125', + '\040', '\163', '\164', '\040', '\061', '\012', '\114', '\161', + '\153', '\040', '\161', '\165', '\040', '\061', '\012', '\156', + '\146', '\111', '\040', '\141', '\156', '\040', '\061', '\012', + '\152', '\171', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\127', '\166', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\123', '\144', '\166', '\040', '\144', '\145', + '\040', '\061', '\012', '\165', '\131', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\121', '\147', '\155', '\040', + '\156', '\147', '\040', '\061', '\012', '\143', '\130', '\141', + '\040', '\143', '\150', '\040', '\061', '\012', '\167', '\102', + '\170', '\040', '\167', '\141', '\040', '\061', '\012', '\160', + '\131', '\170', '\040', '\160', '\162', '\040', '\061', '\012', + '\152', '\127', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\113', '\146', '\167', '\040', '\167', '\141', '\040', + '\061', '\012', '\161', '\152', '\112', '\040', '\161', '\165', + '\040', '\061', '\012', '\120', '\152', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\141', '\152', '\130', '\040', + '\141', '\156', '\040', '\061', '\012', '\163', '\130', '\144', + '\040', '\163', '\164', '\040', '\061', '\012', '\170', '\110', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\170', + '\150', '\101', '\040', '\164', '\150', '\040', '\061', '\012', + '\162', '\107', '\155', '\040', '\145', '\162', '\040', '\061', + '\012', '\121', '\164', '\155', '\040', '\164', '\150', '\040', + '\061', '\012', '\163', '\162', '\131', '\040', '\145', '\162', + '\040', '\061', '\012', '\161', '\120', '\170', '\040', '\161', + '\165', '\040', '\061', '\012', '\167', '\122', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\167', '\117', '\147', + '\040', '\167', '\141', '\040', '\061', '\012', '\146', '\114', + '\147', '\040', '\156', '\147', '\040', '\061', '\012', '\150', + '\121', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\152', '\150', '\127', '\040', '\164', '\150', '\040', '\061', + '\012', '\103', '\167', '\153', '\040', '\153', '\141', '\040', + '\061', '\012', '\172', '\127', '\154', '\040', '\154', '\145', + '\040', '\061', '\012', '\167', '\112', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\120', '\170', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\156', '\160', '\111', + '\040', '\141', '\156', '\040', '\061', '\012', '\154', '\156', + '\127', '\040', '\141', '\156', '\040', '\061', '\012', '\153', + '\161', '\171', '\040', '\161', '\165', '\040', '\061', '\012', + '\171', '\167', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\163', '\103', '\144', '\040', '\163', '\164', '\040', + '\061', '\012', '\161', '\146', '\106', '\040', '\161', '\165', + '\040', '\061', '\012', '\161', '\160', '\147', '\040', '\161', + '\165', '\040', '\061', '\012', '\115', '\142', '\170', '\040', + '\142', '\145', '\040', '\061', '\012', '\156', '\167', '\116', + '\040', '\141', '\156', '\040', '\061', '\012', '\167', '\114', + '\163', '\040', '\163', '\164', '\040', '\061', '\012', '\127', + '\143', '\166', '\040', '\143', '\150', '\040', '\061', '\012', + '\126', '\166', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\126', '\153', '\170', '\040', '\153', '\141', '\040', + '\061', '\012', '\144', '\155', '\125', '\040', '\144', '\145', + '\040', '\061', '\012', '\146', '\107', '\163', '\040', '\163', + '\164', '\040', '\061', '\012', '\147', '\112', '\172', '\040', + '\156', '\147', '\040', '\061', '\012', '\144', '\106', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\161', '\103', + '\146', '\040', '\161', '\165', '\040', '\061', '\012', '\154', + '\166', '\127', '\040', '\154', '\145', '\040', '\061', '\012', + '\123', '\166', '\142', '\040', '\166', '\141', '\040', '\061', + '\012', '\170', '\112', '\162', '\040', '\145', '\162', '\040', + '\061', '\012', '\165', '\132', '\146', '\040', '\161', '\165', + '\040', '\061', '\012', '\124', '\152', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\160', '\111', '\152', '\040', + '\151', '\152', '\040', '\061', '\012', '\142', '\126', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\166', '\144', + '\117', '\040', '\144', '\145', '\040', '\061', '\012', '\154', + '\124', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\142', '\115', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\104', '\155', '\040', '\141', '\156', '\040', + '\061', '\012', '\124', '\172', '\142', '\040', '\163', '\172', + '\040', '\061', '\012', '\160', '\103', '\167', '\040', '\160', + '\162', '\040', '\061', '\012', '\121', '\153', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\146', '\160', '\131', + '\040', '\160', '\162', '\040', '\061', '\012', '\171', '\121', + '\152', '\040', '\151', '\152', '\040', '\061', '\012', '\161', + '\151', '\103', '\040', '\161', '\165', '\040', '\061', '\012', + '\155', '\121', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\167', '\125', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\153', '\126', '\152', '\040', '\151', '\152', + '\040', '\061', '\012', '\164', '\152', '\121', '\040', '\164', + '\150', '\040', '\061', '\012', '\155', '\130', '\152', '\040', + '\151', '\152', '\040', '\061', '\012', '\130', '\146', '\144', + '\040', '\144', '\145', '\040', '\061', '\012', '\143', '\147', + '\111', '\040', '\143', '\150', '\040', '\061', '\012', '\120', + '\153', '\152', '\040', '\151', '\152', '\040', '\061', '\012', + '\152', '\152', '\106', '\040', '\151', '\152', '\040', '\061', + '\012', '\152', '\162', '\112', '\040', '\145', '\162', '\040', + '\061', '\012', '\161', '\167', '\132', '\040', '\161', '\165', + '\040', '\061', '\012', '\122', '\164', '\172', '\040', '\164', + '\150', '\040', '\061', '\012', '\146', '\110', '\142', '\040', + '\142', '\145', '\040', '\061', '\012', '\110', '\147', '\170', + '\040', '\156', '\147', '\040', '\061', '\012', '\104', '\172', + '\146', '\040', '\163', '\172', '\040', '\061', '\012', '\143', + '\142', '\105', '\040', '\143', '\150', '\040', '\061', '\012', + '\130', '\146', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\122', '\152', '\155', '\040', '\151', '\152', '\040', + '\061', '\012', '\146', '\155', '\131', '\040', '\155', '\145', + '\040', '\061', '\012', '\167', '\131', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\165', '\106', '\160', '\040', + '\161', '\165', '\040', '\061', '\012', '\166', '\127', '\155', + '\040', '\166', '\141', '\040', '\061', '\012', '\171', '\126', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\143', + '\147', '\114', '\040', '\143', '\150', '\040', '\061', '\012', + '\172', '\155', '\122', '\040', '\163', '\172', '\040', '\061', + '\012', '\172', '\146', '\102', '\040', '\163', '\172', '\040', + '\061', '\012', '\172', '\156', '\110', '\040', '\141', '\156', + '\040', '\061', '\012', '\150', '\147', '\107', '\040', '\164', + '\150', '\040', '\061', '\012', '\170', '\165', '\105', '\040', + '\161', '\165', '\040', '\061', '\012', '\102', '\163', '\154', + '\040', '\154', '\145', '\040', '\061', '\012', '\157', '\127', + '\170', '\040', '\157', '\156', '\040', '\061', '\012', '\120', + '\152', '\154', '\040', '\154', '\145', '\040', '\061', '\012', + '\112', '\144', '\146', '\040', '\144', '\145', '\040', '\061', + '\012', '\130', '\155', '\160', '\040', '\155', '\145', '\040', + '\061', '\012', '\163', '\147', '\117', '\040', '\156', '\147', + '\040', '\061', '\012', '\150', '\103', '\152', '\040', '\164', + '\150', '\040', '\061', '\012', '\167', '\164', '\122', '\040', + '\164', '\150', '\040', '\061', '\012', '\146', '\104', '\163', + '\040', '\163', '\164', '\040', '\061', '\012', '\142', '\121', + '\142', '\040', '\142', '\145', '\040', '\061', '\012', '\161', + '\165', '\115', '\040', '\165', '\156', '\040', '\061', '\012', + '\146', '\114', '\154', '\040', '\154', '\145', '\040', '\061', + '\012', '\116', '\150', '\160', '\040', '\164', '\150', '\040', + '\061', '\012', '\172', '\156', '\125', '\040', '\141', '\156', + '\040', '\061', '\012', '\163', '\144', '\123', '\040', '\163', + '\164', '\040', '\061', '\012', '\167', '\127', '\165', '\040', + '\161', '\165', '\040', '\061', '\012', '\164', '\106', '\161', + '\040', '\164', '\150', '\040', '\061', '\012', '\143', '\106', + '\161', '\040', '\143', '\150', '\040', '\061', '\012', '\127', + '\167', '\154', '\040', '\154', '\145', '\040', '\061', '\012', + '\114', '\161', '\171', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\161', '\121', '\040', '\141', '\156', '\040', + '\061', '\012', '\172', '\155', '\104', '\040', '\163', '\172', + '\040', '\061', '\012', '\107', '\171', '\170', '\040', '\156', + '\171', '\040', '\061', '\012', '\142', '\153', '\122', '\040', + '\153', '\141', '\040', '\061', '\012', '\154', '\121', '\167', + '\040', '\154', '\145', '\040', '\061', '\012', '\120', '\161', + '\155', '\040', '\161', '\165', '\040', '\061', '\012', '\106', + '\167', '\153', '\040', '\153', '\141', '\040', '\061', '\012', + '\164', '\110', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\152', '\171', '\114', '\040', '\151', '\152', '\040', + '\061', '\012', '\161', '\170', '\101', '\040', '\161', '\165', + '\040', '\061', '\012', '\155', '\162', '\103', '\040', '\145', + '\162', '\040', '\061', '\012', '\161', '\172', '\114', '\040', + '\161', '\165', '\040', '\061', '\012', '\152', '\112', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\152', '\146', + '\123', '\040', '\151', '\152', '\040', '\061', '\012', '\161', + '\115', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\155', '\154', '\126', '\040', '\154', '\145', '\040', '\061', + '\012', '\142', '\153', '\112', '\040', '\153', '\141', '\040', + '\061', '\012', '\153', '\156', '\110', '\040', '\141', '\156', + '\040', '\061', '\012', '\125', '\161', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\143', '\165', '\106', '\040', + '\143', '\150', '\040', '\061', '\012', '\151', '\131', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\146', '\125', + '\145', '\040', '\145', '\162', '\040', '\061', '\012', '\163', + '\102', '\142', '\040', '\163', '\164', '\040', '\061', '\012', + '\116', '\150', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\162', '\150', '\120', '\040', '\164', '\150', '\040', + '\061', '\012', '\144', '\127', '\160', '\040', '\144', '\145', + '\040', '\061', '\012', '\131', '\166', '\146', '\040', '\166', + '\141', '\040', '\061', '\012', '\122', '\170', '\162', '\040', + '\145', '\162', '\040', '\061', '\012', '\153', '\172', '\107', + '\040', '\163', '\172', '\040', '\061', '\012', '\170', '\165', + '\132', '\040', '\161', '\165', '\040', '\061', '\012', '\170', + '\166', '\104', '\040', '\166', '\141', '\040', '\061', '\012', + '\146', '\167', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\152', '\112', '\040', '\164', '\150', '\040', + '\061', '\012', '\153', '\132', '\162', '\040', '\145', '\162', + '\040', '\061', '\012', '\166', '\112', '\156', '\040', '\141', + '\156', '\040', '\061', '\012', '\170', '\156', '\117', '\040', + '\141', '\156', '\040', '\061', '\012', '\166', '\143', '\101', + '\040', '\143', '\150', '\040', '\061', '\012', '\155', '\146', + '\113', '\040', '\155', '\145', '\040', '\061', '\012', '\166', + '\152', '\123', '\040', '\151', '\152', '\040', '\061', '\012', + '\116', '\166', '\160', '\040', '\166', '\141', '\040', '\061', + '\012', '\144', '\146', '\102', '\040', '\144', '\145', '\040', + '\061', '\012', '\121', '\163', '\142', '\040', '\163', '\164', + '\040', '\061', '\012', '\144', '\130', '\160', '\040', '\160', + '\162', '\040', '\061', '\012', '\172', '\122', '\154', '\040', + '\154', '\145', '\040', '\061', '\012', '\105', '\152', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\141', '\107', + '\172', '\040', '\141', '\156', '\040', '\061', '\012', '\156', + '\110', '\147', '\040', '\141', '\156', '\040', '\061', '\012', + '\142', '\166', '\101', '\040', '\166', '\141', '\040', '\061', + '\012', '\102', '\146', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\172', '\126', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\172', '\163', '\131', '\040', '\163', + '\164', '\040', '\061', '\012', '\150', '\126', '\172', '\040', + '\164', '\150', '\040', '\061', '\012', '\120', '\152', '\155', + '\040', '\151', '\152', '\040', '\061', '\012', '\163', '\130', + '\151', '\040', '\151', '\156', '\040', '\061', '\012', '\151', + '\113', '\152', '\040', '\151', '\156', '\040', '\061', '\012', + '\161', '\141', '\105', '\040', '\141', '\156', '\040', '\061', + '\012', '\103', '\146', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\172', '\115', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\155', '\147', '\132', '\040', '\156', + '\147', '\040', '\061', '\012', '\166', '\147', '\101', '\040', + '\156', '\147', '\040', '\061', '\012', '\151', '\167', '\112', + '\040', '\151', '\156', '\040', '\061', '\012', '\166', '\107', + '\170', '\040', '\166', '\141', '\040', '\061', '\012', '\164', + '\146', '\131', '\040', '\164', '\150', '\040', '\061', '\012', + '\154', '\152', '\110', '\040', '\154', '\145', '\040', '\061', + '\012', '\172', '\107', '\152', '\040', '\163', '\172', '\040', + '\061', '\012', '\142', '\155', '\113', '\040', '\155', '\145', + '\040', '\061', '\012', '\156', '\125', '\161', '\040', '\141', + '\156', '\040', '\061', '\012', '\172', '\122', '\164', '\040', + '\164', '\150', '\040', '\061', '\012', '\164', '\107', '\152', + '\040', '\164', '\150', '\040', '\061', '\012', '\172', '\126', + '\144', '\040', '\163', '\172', '\040', '\061', '\012', '\152', + '\123', '\162', '\040', '\145', '\162', '\040', '\061', '\012', + '\146', '\116', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\124', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\156', '\161', '\105', '\040', '\141', '\156', + '\040', '\061', '\012', '\127', '\156', '\147', '\040', '\141', + '\156', '\040', '\061', '\012', '\172', '\126', '\166', '\040', + '\163', '\172', '\040', '\061', '\012', '\147', '\126', '\163', + '\040', '\156', '\147', '\040', '\061', '\012', '\146', '\116', + '\144', '\040', '\144', '\145', '\040', '\061', '\012', '\161', + '\116', '\167', '\040', '\161', '\165', '\040', '\061', '\012', + '\132', '\156', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\165', '\112', '\163', '\040', '\161', '\165', '\040', + '\061', '\012', '\171', '\166', '\112', '\040', '\166', '\141', + '\040', '\061', '\012', '\170', '\154', '\115', '\040', '\154', + '\145', '\040', '\061', '\012', '\112', '\172', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\166', '\122', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\146', '\143', + '\113', '\040', '\143', '\150', '\040', '\061', '\012', '\167', + '\126', '\156', '\040', '\141', '\156', '\040', '\061', '\012', + '\162', '\127', '\167', '\040', '\145', '\162', '\040', '\061', + '\012', '\143', '\110', '\153', '\040', '\143', '\150', '\040', + '\061', '\012', '\166', '\117', '\170', '\040', '\166', '\141', + '\040', '\061', '\012', '\151', '\125', '\141', '\040', '\141', + '\156', '\040', '\061', '\012', '\156', '\127', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\172', '\161', '\132', + '\040', '\161', '\165', '\040', '\061', '\012', '\170', '\106', + '\152', '\040', '\151', '\152', '\040', '\061', '\012', '\156', + '\103', '\147', '\040', '\141', '\156', '\040', '\061', '\012', + '\146', '\131', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\126', '\163', '\170', '\040', '\163', '\164', '\040', + '\061', '\012', '\155', '\164', '\115', '\040', '\164', '\150', + '\040', '\061', '\012', '\155', '\150', '\107', '\040', '\164', + '\150', '\040', '\061', '\012', '\152', '\164', '\116', '\040', + '\164', '\150', '\040', '\061', '\012', '\150', '\143', '\103', + '\040', '\164', '\150', '\040', '\061', '\012', '\116', '\167', + '\153', '\040', '\153', '\141', '\040', '\061', '\012', '\144', + '\130', '\165', '\040', '\161', '\165', '\040', '\061', '\012', + '\155', '\112', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\163', '\117', '\040', '\163', '\164', '\040', + '\061', '\012', '\161', '\122', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\122', '\156', '\152', '\040', '\141', + '\156', '\040', '\061', '\012', '\153', '\155', '\120', '\040', + '\153', '\141', '\040', '\061', '\012', '\130', '\164', '\147', + '\040', '\164', '\150', '\040', '\061', '\012', '\107', '\166', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\152', + '\161', '\166', '\040', '\161', '\165', '\040', '\061', '\012', + '\143', '\126', '\154', '\040', '\143', '\150', '\040', '\061', + '\012', '\143', '\144', '\111', '\040', '\143', '\150', '\040', + '\061', '\012', '\172', '\144', '\105', '\040', '\163', '\172', + '\040', '\061', '\012', '\150', '\132', '\153', '\040', '\164', + '\150', '\040', '\061', '\012', '\102', '\144', '\170', '\040', + '\144', '\145', '\040', '\061', '\012', '\150', '\110', '\156', + '\040', '\164', '\150', '\040', '\061', '\012', '\150', '\153', + '\107', '\040', '\164', '\150', '\040', '\061', '\012', '\166', + '\170', '\112', '\040', '\166', '\141', '\040', '\061', '\012', + '\154', '\162', '\101', '\040', '\145', '\162', '\040', '\061', + '\012', '\154', '\162', '\124', '\040', '\145', '\162', '\040', + '\061', '\012', '\150', '\152', '\126', '\040', '\164', '\150', + '\040', '\061', '\012', '\161', '\142', '\111', '\040', '\161', + '\165', '\040', '\061', '\012', '\155', '\124', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\146', '\155', '\126', + '\040', '\155', '\145', '\040', '\061', '\012', '\162', '\104', + '\153', '\040', '\145', '\162', '\040', '\061', '\012', '\144', + '\116', '\144', '\040', '\144', '\145', '\040', '\061', '\012', + '\107', '\172', '\152', '\040', '\163', '\172', '\040', '\061', + '\012', '\141', '\126', '\152', '\040', '\141', '\156', '\040', + '\061', '\012', '\166', '\116', '\162', '\040', '\145', '\162', + '\040', '\061', '\012', '\153', '\130', '\141', '\040', '\141', + '\156', '\040', '\061', '\012', '\162', '\107', '\163', '\040', + '\145', '\162', '\040', '\061', '\012', '\170', '\141', '\130', + '\040', '\141', '\156', '\040', '\061', '\012', '\143', '\162', + '\107', '\040', '\143', '\150', '\040', '\061', '\012', '\161', + '\112', '\141', '\040', '\141', '\156', '\040', '\061', '\012', + '\152', '\104', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\115', '\146', '\170', '\040', '\146', '\157', '\040', + '\061', '\012', '\170', '\105', '\141', '\040', '\141', '\156', + '\040', '\061', '\012', '\121', '\166', '\172', '\040', '\163', + '\172', '\040', '\061', '\012', '\167', '\122', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\160', '\106', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\103', '\160', + '\166', '\040', '\166', '\141', '\040', '\061', '\012', '\162', + '\112', '\153', '\040', '\145', '\162', '\040', '\061', '\012', + '\146', '\142', '\121', '\040', '\142', '\145', '\040', '\061', + '\012', '\130', '\172', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\161', '\106', '\171', '\040', '\161', '\165', + '\040', '\061', '\012', '\132', '\146', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\164', '\167', '\105', '\040', + '\164', '\150', '\040', '\061', '\012', '\117', '\141', '\161', + '\040', '\141', '\156', '\040', '\061', '\012', '\171', '\163', + '\131', '\040', '\163', '\164', '\040', '\061', '\012', '\167', + '\144', '\132', '\040', '\144', '\145', '\040', '\061', '\012', + '\147', '\155', '\117', '\040', '\156', '\147', '\040', '\061', + '\012', '\167', '\107', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\167', '\122', '\153', '\040', '\153', '\141', + '\040', '\061', '\012', '\147', '\161', '\123', '\040', '\161', + '\165', '\040', '\061', '\012', '\101', '\147', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\124', '\167', '\166', + '\040', '\166', '\141', '\040', '\061', '\012', '\121', '\156', + '\166', '\040', '\141', '\156', '\040', '\061', '\012', '\142', + '\126', '\166', '\040', '\166', '\141', '\040', '\061', '\012', + '\143', '\104', '\167', '\040', '\143', '\150', '\040', '\061', + '\012', '\164', '\107', '\161', '\040', '\164', '\150', '\040', + '\061', '\012', '\146', '\142', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\124', '\166', '\167', '\040', '\166', + '\141', '\040', '\061', '\012', '\155', '\116', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\144', '\164', '\105', + '\040', '\164', '\150', '\040', '\061', '\012', '\160', '\172', + '\120', '\040', '\163', '\172', '\040', '\061', '\012', '\126', + '\163', '\167', '\040', '\163', '\172', '\040', '\061', '\012', + '\161', '\107', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\120', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\161', '\171', '\103', '\040', '\161', '\165', + '\040', '\061', '\012', '\156', '\170', '\106', '\040', '\141', + '\156', '\040', '\061', '\012', '\152', '\104', '\154', '\040', + '\154', '\145', '\040', '\061', '\012', '\152', '\110', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\146', '\170', + '\132', '\040', '\146', '\157', '\040', '\061', '\012', '\163', + '\121', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\156', '\155', '\110', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\162', '\104', '\040', '\145', '\162', '\040', + '\061', '\012', '\150', '\115', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\166', '\110', '\153', '\040', '\153', + '\141', '\040', '\061', '\012', '\150', '\155', '\123', '\040', + '\164', '\150', '\040', '\061', '\012', '\130', '\144', '\164', + '\040', '\164', '\150', '\040', '\061', '\012', '\130', '\167', + '\154', '\040', '\154', '\145', '\040', '\061', '\012', '\165', + '\112', '\162', '\040', '\161', '\165', '\040', '\061', '\012', + '\163', '\120', '\153', '\040', '\163', '\164', '\040', '\061', + '\012', '\130', '\152', '\160', '\040', '\151', '\152', '\040', + '\061', '\012', '\125', '\161', '\151', '\040', '\161', '\165', + '\040', '\061', '\012', '\153', '\147', '\104', '\040', '\156', + '\147', '\040', '\061', '\012', '\152', '\147', '\111', '\040', + '\156', '\147', '\040', '\061', '\012', '\165', '\106', '\167', + '\040', '\161', '\165', '\040', '\061', '\012', '\170', '\116', + '\144', '\040', '\144', '\145', '\040', '\061', '\012', '\144', + '\150', '\111', '\040', '\164', '\150', '\040', '\061', '\012', + '\114', '\170', '\157', '\040', '\157', '\156', '\040', '\061', + '\012', '\123', '\146', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\172', '\122', '\160', '\040', '\163', '\172', + '\040', '\061', '\012', '\170', '\167', '\113', '\040', '\167', + '\141', '\040', '\061', '\012', '\146', '\155', '\102', '\040', + '\155', '\145', '\040', '\061', '\012', '\166', '\162', '\126', + '\040', '\145', '\162', '\040', '\061', '\012', '\161', '\123', + '\146', '\040', '\161', '\165', '\040', '\061', '\012', '\152', + '\120', '\156', '\040', '\141', '\156', '\040', '\061', '\012', + '\110', '\142', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\142', '\112', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\154', '\161', '\121', '\040', '\161', '\165', + '\040', '\061', '\012', '\170', '\123', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\144', '\115', '\153', '\040', + '\144', '\145', '\040', '\061', '\012', '\166', '\126', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\166', '\153', + '\113', '\040', '\153', '\141', '\040', '\061', '\012', '\130', + '\144', '\163', '\040', '\144', '\145', '\040', '\061', '\012', + '\171', '\142', '\102', '\040', '\142', '\145', '\040', '\061', + '\012', '\147', '\160', '\105', '\040', '\156', '\147', '\040', + '\061', '\012', '\161', '\143', '\103', '\040', '\143', '\150', + '\040', '\061', '\012', '\160', '\170', '\114', '\040', '\160', + '\162', '\040', '\061', '\012', '\147', '\120', '\155', '\040', + '\156', '\147', '\040', '\061', '\012', '\102', '\160', '\144', + '\040', '\144', '\145', '\040', '\061', '\012', '\144', '\160', + '\102', '\040', '\144', '\145', '\040', '\061', '\012', '\152', + '\154', '\112', '\040', '\154', '\145', '\040', '\061', '\012', + '\160', '\153', '\103', '\040', '\153', '\141', '\040', '\061', + '\012', '\171', '\160', '\120', '\040', '\160', '\162', '\040', + '\061', '\012', '\116', '\161', '\155', '\040', '\161', '\165', + '\040', '\061', '\012', '\164', '\147', '\132', '\040', '\164', + '\150', '\040', '\061', '\012', '\105', '\161', '\157', '\040', + '\161', '\165', '\040', '\061', '\012', '\144', '\122', '\153', + '\040', '\144', '\145', '\040', '\061', '\012', '\125', '\142', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\170', + '\150', '\131', '\040', '\164', '\150', '\040', '\061', '\012', + '\154', '\112', '\144', '\040', '\154', '\145', '\040', '\061', + '\012', '\160', '\166', '\116', '\040', '\166', '\141', '\040', + '\061', '\012', '\121', '\146', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\104', '\142', '\167', '\040', '\167', + '\141', '\040', '\061', '\012', '\163', '\106', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\167', '\153', '\130', + '\040', '\153', '\141', '\040', '\061', '\012', '\170', '\160', + '\122', '\040', '\160', '\162', '\040', '\061', '\012', '\160', + '\152', '\112', '\040', '\151', '\152', '\040', '\061', '\012', + '\147', '\153', '\121', '\040', '\156', '\147', '\040', '\061', + '\012', '\162', '\115', '\146', '\040', '\145', '\162', '\040', + '\061', '\012', '\112', '\163', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\170', '\117', '\167', '\040', '\167', + '\141', '\040', '\061', '\012', '\104', '\161', '\165', '\040', + '\165', '\156', '\040', '\061', '\012', '\156', '\142', '\112', + '\040', '\141', '\156', '\040', '\061', '\012', '\147', '\166', + '\106', '\040', '\156', '\147', '\040', '\061', '\012', '\106', + '\156', '\160', '\040', '\141', '\156', '\040', '\061', '\012', + '\152', '\160', '\126', '\040', '\151', '\152', '\040', '\061', + '\012', '\161', '\164', '\104', '\040', '\164', '\150', '\040', + '\061', '\012', '\165', '\105', '\152', '\040', '\161', '\165', + '\040', '\061', '\012', '\171', '\150', '\131', '\040', '\164', + '\150', '\040', '\061', '\012', '\117', '\150', '\161', '\040', + '\164', '\150', '\040', '\061', '\012', '\156', '\130', '\171', + '\040', '\141', '\156', '\040', '\061', '\012', '\160', '\144', + '\125', '\040', '\144', '\145', '\040', '\061', '\012', '\155', + '\104', '\172', '\040', '\163', '\172', '\040', '\061', '\012', + '\151', '\126', '\153', '\040', '\151', '\156', '\040', '\061', + '\012', '\110', '\161', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\170', '\160', '\132', '\040', '\160', '\157', + '\040', '\061', '\012', '\141', '\145', '\125', '\040', '\141', + '\156', '\040', '\061', '\012', '\163', '\152', '\132', '\040', + '\163', '\164', '\040', '\061', '\012', '\163', '\107', '\160', + '\040', '\163', '\164', '\040', '\061', '\012', '\127', '\161', + '\156', '\040', '\141', '\156', '\040', '\061', '\012', '\170', + '\161', '\123', '\040', '\161', '\165', '\040', '\061', '\012', + '\112', '\152', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\120', '\160', '\040', '\161', '\165', '\040', + '\061', '\012', '\163', '\130', '\172', '\040', '\163', '\164', + '\040', '\061', '\012', '\170', '\166', '\120', '\040', '\166', + '\141', '\040', '\061', '\012', '\127', '\142', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\164', '\152', '\113', + '\040', '\164', '\150', '\040', '\061', '\012', '\154', '\150', + '\110', '\040', '\164', '\150', '\040', '\061', '\012', '\150', + '\161', '\126', '\040', '\164', '\150', '\040', '\061', '\012', + '\144', '\131', '\146', '\040', '\144', '\145', '\040', '\061', + '\012', '\160', '\106', '\153', '\040', '\153', '\141', '\040', + '\061', '\012', '\163', '\106', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\165', '\110', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\166', '\150', '\101', '\040', + '\164', '\150', '\040', '\061', '\012', '\152', '\154', '\105', + '\040', '\154', '\145', '\040', '\061', '\012', '\163', '\161', + '\102', '\040', '\161', '\165', '\040', '\061', '\012', '\161', + '\156', '\162', '\040', '\141', '\156', '\040', '\061', '\012', + '\106', '\170', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\172', '\110', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\160', '\144', '\102', '\040', '\144', '\145', + '\040', '\061', '\012', '\167', '\110', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\120', '\170', '\152', '\040', + '\151', '\152', '\040', '\061', '\012', '\147', '\110', '\170', + '\040', '\156', '\147', '\040', '\061', '\012', '\156', '\161', + '\112', '\040', '\141', '\156', '\040', '\061', '\012', '\157', + '\161', '\130', '\040', '\161', '\165', '\040', '\061', '\012', + '\130', '\142', '\171', '\040', '\142', '\145', '\040', '\061', + '\012', '\164', '\142', '\111', '\040', '\164', '\150', '\040', + '\061', '\012', '\153', '\123', '\146', '\040', '\153', '\141', + '\040', '\061', '\012', '\166', '\150', '\104', '\040', '\164', + '\150', '\040', '\061', '\012', '\161', '\110', '\152', '\040', + '\161', '\165', '\040', '\061', '\012', '\116', '\160', '\170', + '\040', '\160', '\162', '\040', '\061', '\012', '\121', '\172', + '\160', '\040', '\163', '\172', '\040', '\061', '\012', '\170', + '\151', '\125', '\040', '\151', '\156', '\040', '\061', '\012', + '\162', '\152', '\132', '\040', '\145', '\162', '\040', '\061', + '\012', '\167', '\152', '\125', '\040', '\151', '\152', '\040', + '\061', '\012', '\152', '\164', '\102', '\040', '\164', '\150', + '\040', '\061', '\012', '\131', '\147', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\141', '\121', '\146', '\040', + '\141', '\156', '\040', '\061', '\012', '\170', '\127', '\165', + '\040', '\161', '\165', '\040', '\061', '\012', '\141', '\126', + '\146', '\040', '\141', '\156', '\040', '\061', '\012', '\160', + '\121', '\170', '\040', '\160', '\162', '\040', '\061', '\012', + '\114', '\156', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\127', '\141', '\040', '\141', '\156', '\040', + '\061', '\012', '\165', '\110', '\160', '\040', '\161', '\165', + '\040', '\061', '\012', '\114', '\166', '\160', '\040', '\166', + '\141', '\040', '\061', '\012', '\112', '\170', '\160', '\040', + '\160', '\162', '\040', '\061', '\012', '\172', '\110', '\153', + '\040', '\163', '\172', '\040', '\061', '\012', '\167', '\166', + '\125', '\040', '\166', '\141', '\040', '\061', '\012', '\127', + '\161', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\150', '\126', '\163', '\040', '\164', '\150', '\040', '\061', + '\012', '\130', '\147', '\171', '\040', '\156', '\147', '\040', + '\061', '\012', '\144', '\132', '\152', '\040', '\144', '\145', + '\040', '\061', '\012', '\165', '\103', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\107', '\170', '\154', '\040', + '\154', '\145', '\040', '\061', '\012', '\110', '\154', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\127', '\161', + '\144', '\040', '\161', '\165', '\040', '\061', '\012', '\104', + '\170', '\172', '\040', '\163', '\172', '\040', '\061', '\012', + '\150', '\144', '\116', '\040', '\164', '\150', '\040', '\061', + '\012', '\160', '\166', '\115', '\040', '\166', '\141', '\040', + '\061', '\012', '\127', '\170', '\153', '\040', '\153', '\141', + '\040', '\061', '\012', '\161', '\127', '\144', '\040', '\161', + '\165', '\040', '\061', '\012', '\146', '\151', '\117', '\040', + '\151', '\156', '\040', '\061', '\012', '\146', '\104', '\167', + '\040', '\167', '\141', '\040', '\061', '\012', '\142', '\110', + '\152', '\040', '\151', '\152', '\040', '\061', '\012', '\151', + '\126', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\120', '\155', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\146', '\130', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\170', '\146', '\114', '\040', '\146', '\157', + '\040', '\061', '\012', '\171', '\107', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\171', '\102', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\150', '\103', '\153', + '\040', '\164', '\150', '\040', '\061', '\012', '\114', '\154', + '\153', '\040', '\154', '\145', '\040', '\061', '\012', '\171', + '\115', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\161', '\162', '\131', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\144', '\130', '\040', '\156', '\147', '\040', + '\061', '\012', '\161', '\170', '\107', '\040', '\161', '\165', + '\040', '\061', '\012', '\132', '\155', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\122', '\172', '\167', '\040', + '\163', '\172', '\040', '\061', '\012', '\156', '\102', '\144', + '\040', '\141', '\156', '\040', '\061', '\012', '\155', '\127', + '\154', '\040', '\154', '\145', '\040', '\061', '\012', '\170', + '\165', '\111', '\040', '\161', '\165', '\040', '\061', '\012', + '\152', '\171', '\106', '\040', '\151', '\152', '\040', '\061', + '\012', '\142', '\126', '\165', '\040', '\161', '\165', '\040', + '\061', '\012', '\171', '\147', '\120', '\040', '\156', '\147', + '\040', '\061', '\012', '\144', '\106', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\152', '\106', '\155', '\040', + '\151', '\152', '\040', '\061', '\012', '\122', '\155', '\154', + '\040', '\154', '\145', '\040', '\061', '\012', '\153', '\154', + '\110', '\040', '\154', '\145', '\040', '\061', '\012', '\126', + '\146', '\146', '\040', '\146', '\157', '\040', '\061', '\012', + '\113', '\172', '\153', '\040', '\163', '\172', '\040', '\061', + '\012', '\114', '\150', '\166', '\040', '\164', '\150', '\040', + '\061', '\012', '\143', '\123', '\152', '\040', '\143', '\150', + '\040', '\061', '\012', '\121', '\162', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\165', '\102', '\167', '\040', + '\161', '\165', '\040', '\061', '\012', '\163', '\103', '\153', + '\040', '\153', '\141', '\040', '\061', '\012', '\161', '\171', + '\123', '\040', '\161', '\165', '\040', '\061', '\012', '\143', + '\130', '\165', '\040', '\143', '\150', '\040', '\061', '\012', + '\167', '\146', '\115', '\040', '\167', '\141', '\040', '\061', + '\012', '\153', '\144', '\113', '\040', '\144', '\145', '\040', + '\061', '\012', '\143', '\130', '\152', '\040', '\143', '\150', + '\040', '\061', '\012', '\143', '\164', '\132', '\040', '\164', + '\150', '\040', '\061', '\012', '\146', '\152', '\111', '\040', + '\151', '\152', '\040', '\061', '\012', '\143', '\147', '\123', + '\040', '\143', '\150', '\040', '\061', '\012', '\155', '\167', + '\114', '\040', '\155', '\145', '\040', '\061', '\012', '\153', + '\172', '\125', '\040', '\163', '\172', '\040', '\061', '\012', + '\143', '\132', '\162', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\161', '\125', '\040', '\161', '\165', '\040', + '\061', '\012', '\161', '\112', '\151', '\040', '\161', '\165', + '\040', '\061', '\012', '\147', '\104', '\144', '\040', '\156', + '\147', '\040', '\061', '\012', '\142', '\113', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\141', '\125', '\167', + '\040', '\141', '\156', '\040', '\061', '\012', '\163', '\170', + '\105', '\040', '\163', '\164', '\040', '\061', '\012', '\155', + '\170', '\125', '\040', '\155', '\145', '\040', '\061', '\012', + '\143', '\167', '\131', '\040', '\143', '\150', '\040', '\061', + '\012', '\146', '\160', '\103', '\040', '\160', '\162', '\040', + '\061', '\012', '\163', '\122', '\167', '\040', '\163', '\164', + '\040', '\061', '\012', '\113', '\153', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\167', '\170', '\101', '\040', + '\167', '\141', '\040', '\061', '\012', '\147', '\121', '\146', + '\040', '\156', '\147', '\040', '\061', '\012', '\160', '\120', + '\142', '\040', '\160', '\162', '\040', '\061', '\012', '\110', + '\167', '\165', '\040', '\153', '\165', '\040', '\061', '\012', + '\163', '\165', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\161', '\131', '\040', '\161', '\165', '\040', + '\061', '\012', '\163', '\170', '\127', '\040', '\163', '\164', + '\040', '\061', '\012', '\141', '\106', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\154', '\127', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\160', '\142', '\132', + '\040', '\160', '\162', '\040', '\061', '\012', '\142', '\161', + '\155', '\040', '\161', '\165', '\040', '\061', '\012', '\153', + '\112', '\153', '\040', '\153', '\141', '\040', '\061', '\012', + '\161', '\164', '\124', '\040', '\164', '\150', '\040', '\061', + '\012', '\172', '\115', '\144', '\040', '\163', '\172', '\040', + '\061', '\012', '\150', '\107', '\163', '\040', '\164', '\150', + '\040', '\061', '\012', '\170', '\154', '\110', '\040', '\154', + '\145', '\040', '\061', '\012', '\144', '\155', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\130', '\162', '\153', + '\040', '\145', '\162', '\040', '\061', '\012', '\117', '\143', + '\146', '\040', '\143', '\150', '\040', '\061', '\012', '\155', + '\113', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\172', '\162', '\101', '\040', '\145', '\162', '\040', '\061', + '\012', '\147', '\170', '\105', '\040', '\156', '\147', '\040', + '\061', '\012', '\161', '\127', '\165', '\040', '\165', '\156', + '\040', '\061', '\012', '\170', '\121', '\146', '\040', '\146', + '\157', '\040', '\061', '\012', '\130', '\157', '\172', '\040', + '\157', '\156', '\040', '\061', '\012', '\146', '\155', '\120', + '\040', '\155', '\145', '\040', '\061', '\012', '\153', '\144', + '\104', '\040', '\144', '\145', '\040', '\061', '\012', '\142', + '\102', '\172', '\040', '\163', '\172', '\040', '\061', '\012', + '\167', '\160', '\101', '\040', '\160', '\162', '\040', '\061', + '\012', '\156', '\115', '\142', '\040', '\141', '\156', '\040', + '\061', '\012', '\164', '\110', '\161', '\040', '\164', '\150', + '\040', '\061', '\012', '\152', '\115', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\123', '\166', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\152', '\115', '\154', + '\040', '\154', '\145', '\040', '\061', '\012', '\167', '\102', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\171', + '\155', '\130', '\040', '\155', '\145', '\040', '\061', '\012', + '\150', '\143', '\102', '\040', '\164', '\150', '\040', '\061', + '\012', '\142', '\162', '\125', '\040', '\145', '\162', '\040', + '\061', '\012', '\160', '\141', '\130', '\040', '\141', '\156', + '\040', '\061', '\012', '\150', '\144', '\107', '\040', '\164', + '\150', '\040', '\061', '\012', '\106', '\167', '\160', '\040', + '\160', '\162', '\040', '\061', '\012', '\163', '\142', '\131', + '\040', '\163', '\164', '\040', '\061', '\012', '\155', '\150', + '\102', '\040', '\164', '\150', '\040', '\061', '\012', '\160', + '\146', '\132', '\040', '\160', '\162', '\040', '\061', '\012', + '\126', '\155', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\163', '\103', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\132', '\146', '\167', '\040', '\167', '\141', + '\040', '\061', '\012', '\114', '\152', '\155', '\040', '\151', + '\152', '\040', '\061', '\012', '\160', '\161', '\107', '\040', + '\161', '\165', '\040', '\061', '\012', '\144', '\160', '\113', + '\040', '\144', '\145', '\040', '\061', '\012', '\164', '\146', + '\107', '\040', '\164', '\150', '\040', '\061', '\012', '\151', + '\152', '\122', '\040', '\151', '\156', '\040', '\061', '\012', + '\151', '\112', '\171', '\040', '\151', '\156', '\040', '\061', + '\012', '\161', '\146', '\116', '\040', '\161', '\165', '\040', + '\061', '\012', '\143', '\162', '\123', '\040', '\143', '\150', + '\040', '\061', '\012', '\143', '\147', '\124', '\040', '\143', + '\150', '\040', '\061', '\012', '\167', '\117', '\164', '\040', + '\164', '\150', '\040', '\061', '\012', '\146', '\156', '\105', + '\040', '\141', '\156', '\040', '\061', '\012', '\150', '\127', + '\160', '\040', '\164', '\150', '\040', '\061', '\012', '\132', + '\160', '\167', '\040', '\160', '\162', '\040', '\061', '\012', + '\167', '\144', '\117', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\131', '\171', '\040', '\166', '\141', '\040', + '\061', '\012', '\161', '\162', '\111', '\040', '\161', '\165', + '\040', '\061', '\012', '\144', '\155', '\106', '\040', '\144', + '\145', '\040', '\061', '\012', '\152', '\150', '\112', '\040', + '\164', '\150', '\040', '\061', '\012', '\167', '\110', '\162', + '\040', '\145', '\162', '\040', '\061', '\012', '\112', '\172', + '\142', '\040', '\163', '\172', '\040', '\061', '\012', '\146', + '\105', '\171', '\040', '\156', '\171', '\040', '\061', '\012', + '\150', '\150', '\132', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\160', '\121', '\040', '\160', '\162', '\040', + '\061', '\012', '\161', '\131', '\147', '\040', '\161', '\165', + '\040', '\061', '\012', '\161', '\164', '\131', '\040', '\164', + '\150', '\040', '\061', '\012', '\113', '\144', '\170', '\040', + '\144', '\145', '\040', '\061', '\012', '\161', '\146', '\152', + '\040', '\161', '\165', '\040', '\061', '\012', '\122', '\142', + '\166', '\040', '\166', '\141', '\040', '\061', '\012', '\142', + '\142', '\117', '\040', '\142', '\145', '\040', '\061', '\012', + '\130', '\143', '\156', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\103', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\107', '\143', '\170', '\040', '\143', '\150', + '\040', '\061', '\012', '\172', '\155', '\103', '\040', '\163', + '\172', '\040', '\061', '\012', '\167', '\112', '\154', '\040', + '\154', '\145', '\040', '\061', '\012', '\161', '\104', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\112', '\172', + '\162', '\040', '\145', '\162', '\040', '\061', '\012', '\131', + '\162', '\167', '\040', '\145', '\162', '\040', '\061', '\012', + '\113', '\163', '\170', '\040', '\163', '\164', '\040', '\061', + '\012', '\165', '\113', '\170', '\040', '\161', '\165', '\040', + '\061', '\012', '\152', '\123', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\114', '\152', '\172', '\040', '\163', + '\172', '\040', '\061', '\012', '\170', '\144', '\102', '\040', + '\144', '\145', '\040', '\061', '\012', '\172', '\127', '\142', + '\040', '\163', '\172', '\040', '\061', '\012', '\166', '\167', + '\131', '\040', '\166', '\141', '\040', '\061', '\012', '\166', + '\115', '\144', '\040', '\144', '\145', '\040', '\061', '\012', + '\144', '\142', '\110', '\040', '\144', '\145', '\040', '\061', + '\012', '\121', '\163', '\165', '\040', '\161', '\165', '\040', + '\061', '\012', '\167', '\110', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\147', '\112', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\167', '\132', '\160', '\040', + '\160', '\162', '\040', '\061', '\012', '\142', '\164', '\117', + '\040', '\164', '\150', '\040', '\061', '\012', '\130', '\155', + '\166', '\040', '\166', '\141', '\040', '\061', '\012', '\161', + '\160', '\144', '\040', '\161', '\165', '\040', '\061', '\012', + '\112', '\156', '\167', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\154', '\104', '\040', '\154', '\145', '\040', + '\061', '\012', '\170', '\143', '\130', '\040', '\143', '\150', + '\040', '\061', '\012', '\131', '\166', '\166', '\040', '\166', + '\141', '\040', '\061', '\012', '\132', '\146', '\164', '\040', + '\164', '\150', '\040', '\061', '\012', '\110', '\161', '\172', + '\040', '\161', '\165', '\040', '\061', '\012', '\170', '\161', + '\115', '\040', '\161', '\165', '\040', '\061', '\012', '\110', + '\164', '\150', '\040', '\143', '\150', '\040', '\061', '\012', + '\172', '\164', '\114', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\117', '\152', '\040', '\151', '\156', '\040', + '\061', '\012', '\143', '\111', '\172', '\040', '\143', '\150', + '\040', '\061', '\012', '\150', '\150', '\103', '\040', '\164', + '\150', '\040', '\061', '\012', '\164', '\166', '\130', '\040', + '\164', '\150', '\040', '\061', '\012', '\106', '\147', '\153', + '\040', '\156', '\147', '\040', '\061', '\012', '\155', '\152', + '\103', '\040', '\151', '\152', '\040', '\061', '\012', '\117', + '\152', '\160', '\040', '\151', '\152', '\040', '\061', '\012', + '\153', '\166', '\111', '\040', '\153', '\141', '\040', '\061', + '\012', '\172', '\161', '\142', '\040', '\161', '\165', '\040', + '\061', '\012', '\161', '\161', '\127', '\040', '\161', '\165', + '\040', '\061', '\012', '\151', '\110', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\152', '\170', '\112', '\040', + '\151', '\152', '\040', '\061', '\012', '\107', '\142', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\156', '\121', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\160', + '\130', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\152', '\104', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\161', '\121', '\162', '\040', '\161', '\165', '\040', + '\061', '\012', '\166', '\112', '\170', '\040', '\166', '\141', + '\040', '\061', '\012', '\172', '\142', '\131', '\040', '\163', + '\172', '\040', '\061', '\012', '\146', '\122', '\155', '\040', + '\155', '\145', '\040', '\061', '\012', '\161', '\105', '\154', + '\040', '\161', '\165', '\040', '\061', '\012', '\157', '\141', + '\132', '\040', '\141', '\156', '\040', '\061', '\012', '\166', + '\152', '\106', '\040', '\151', '\152', '\040', '\061', '\012', + '\154', '\161', '\130', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\123', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\142', '\130', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\152', '\112', '\166', '\040', '\151', + '\152', '\040', '\061', '\012', '\127', '\162', '\166', '\040', + '\145', '\162', '\040', '\061', '\012', '\113', '\160', '\167', + '\040', '\160', '\162', '\040', '\061', '\012', '\170', '\141', + '\131', '\040', '\141', '\156', '\040', '\061', '\012', '\152', + '\103', '\166', '\040', '\151', '\152', '\040', '\061', '\012', + '\146', '\142', '\122', '\040', '\142', '\145', '\040', '\061', + '\012', '\160', '\124', '\160', '\040', '\160', '\162', '\040', + '\061', '\012', '\167', '\144', '\111', '\040', '\144', '\145', + '\040', '\061', '\012', '\161', '\146', '\121', '\040', '\161', + '\165', '\040', '\061', '\012', '\122', '\162', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\144', '\142', '\106', + '\040', '\144', '\145', '\040', '\061', '\012', '\142', '\172', + '\106', '\040', '\163', '\172', '\040', '\061', '\012', '\161', + '\167', '\117', '\040', '\161', '\165', '\040', '\061', '\012', + '\166', '\162', '\131', '\040', '\145', '\162', '\040', '\061', + '\012', '\164', '\167', '\111', '\040', '\164', '\150', '\040', + '\061', '\012', '\172', '\114', '\146', '\040', '\163', '\172', + '\040', '\061', '\012', '\142', '\126', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\130', '\156', '\154', '\040', + '\141', '\156', '\040', '\061', '\012', '\127', '\147', '\142', + '\040', '\156', '\147', '\040', '\061', '\012', '\146', '\165', + '\123', '\040', '\161', '\165', '\040', '\061', '\012', '\166', + '\111', '\146', '\040', '\166', '\141', '\040', '\061', '\012', + '\124', '\167', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\156', '\113', '\144', '\040', '\141', '\156', '\040', + '\061', '\012', '\104', '\153', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\165', '\102', '\144', '\040', '\161', + '\165', '\040', '\061', '\012', '\153', '\117', '\172', '\040', + '\153', '\141', '\040', '\061', '\012', '\172', '\117', '\152', + '\040', '\163', '\172', '\040', '\061', '\012', '\156', '\172', + '\105', '\040', '\141', '\156', '\040', '\061', '\012', '\132', + '\142', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\161', '\115', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\146', '\103', '\040', '\156', '\147', '\040', + '\061', '\012', '\166', '\147', '\104', '\040', '\156', '\147', + '\040', '\061', '\012', '\171', '\164', '\103', '\040', '\164', + '\150', '\040', '\061', '\012', '\155', '\161', '\115', '\040', + '\161', '\165', '\040', '\061', '\012', '\113', '\152', '\156', + '\040', '\141', '\156', '\040', '\061', '\012', '\170', '\142', + '\130', '\040', '\142', '\145', '\040', '\061', '\012', '\172', + '\146', '\110', '\040', '\163', '\172', '\040', '\061', '\012', + '\155', '\167', '\110', '\040', '\155', '\145', '\040', '\061', + '\012', '\172', '\121', '\142', '\040', '\163', '\172', '\040', + '\061', '\012', '\107', '\172', '\153', '\040', '\163', '\172', + '\040', '\061', '\012', '\161', '\163', '\127', '\040', '\161', + '\165', '\040', '\061', '\012', '\153', '\116', '\163', '\040', + '\163', '\164', '\040', '\061', '\012', '\114', '\161', '\172', + '\040', '\161', '\165', '\040', '\061', '\012', '\156', '\155', + '\127', '\040', '\141', '\156', '\040', '\061', '\012', '\161', + '\116', '\170', '\040', '\161', '\165', '\040', '\061', '\012', + '\172', '\143', '\121', '\040', '\143', '\150', '\040', '\061', + '\012', '\161', '\115', '\172', '\040', '\161', '\165', '\040', + '\061', '\012', '\167', '\107', '\172', '\040', '\163', '\172', + '\040', '\061', '\012', '\165', '\103', '\144', '\040', '\161', + '\165', '\040', '\061', '\012', '\102', '\160', '\166', '\040', + '\160', '\162', '\040', '\061', '\012', '\161', '\116', '\145', + '\040', '\161', '\165', '\040', '\061', '\012', '\142', '\160', + '\120', '\040', '\160', '\162', '\040', '\061', '\012', '\154', + '\130', '\146', '\040', '\154', '\145', '\040', '\061', '\012', + '\143', '\114', '\161', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\144', '\130', '\040', '\144', '\145', '\040', + '\061', '\012', '\161', '\172', '\125', '\040', '\161', '\165', + '\040', '\061', '\012', '\113', '\170', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\152', '\166', '\106', '\040', + '\151', '\152', '\040', '\061', '\012', '\162', '\106', '\156', + '\040', '\141', '\156', '\040', '\061', '\012', '\105', '\164', + '\161', '\040', '\164', '\150', '\040', '\061', '\012', '\172', + '\131', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\113', '\163', '\166', '\040', '\163', '\164', '\040', '\061', + '\012', '\146', '\112', '\153', '\040', '\153', '\141', '\040', + '\061', '\012', '\146', '\153', '\103', '\040', '\153', '\141', + '\040', '\061', '\012', '\155', '\170', '\113', '\040', '\155', + '\145', '\040', '\061', '\012', '\146', '\142', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\166', '\162', '\127', + '\040', '\145', '\162', '\040', '\061', '\012', '\155', '\120', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\171', + '\102', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\151', '\103', '\146', '\040', '\151', '\156', '\040', '\061', + '\012', '\163', '\162', '\110', '\040', '\145', '\162', '\040', + '\061', '\012', '\150', '\152', '\102', '\040', '\164', '\150', + '\040', '\061', '\012', '\146', '\143', '\107', '\040', '\143', + '\150', '\040', '\061', '\012', '\106', '\164', '\147', '\040', + '\164', '\150', '\040', '\061', '\012', '\165', '\102', '\160', + '\040', '\161', '\165', '\040', '\061', '\012', '\171', '\161', + '\124', '\040', '\161', '\165', '\040', '\061', '\012', '\144', + '\152', '\106', '\040', '\144', '\145', '\040', '\061', '\012', + '\164', '\147', '\125', '\040', '\164', '\150', '\040', '\061', + '\012', '\127', '\162', '\152', '\040', '\145', '\162', '\040', + '\061', '\012', '\170', '\106', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\171', '\143', '\103', '\040', '\143', + '\150', '\040', '\061', '\012', '\145', '\161', '\101', '\040', + '\161', '\165', '\040', '\061', '\012', '\160', '\142', '\107', + '\040', '\160', '\162', '\040', '\061', '\012', '\103', '\167', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\146', + '\104', '\153', '\040', '\153', '\141', '\040', '\061', '\012', + '\167', '\124', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\170', '\162', '\127', '\040', '\145', '\162', '\040', + '\061', '\012', '\153', '\121', '\163', '\040', '\163', '\164', + '\040', '\061', '\012', '\167', '\115', '\154', '\040', '\154', + '\145', '\040', '\061', '\012', '\171', '\103', '\156', '\040', + '\156', '\144', '\040', '\061', '\012', '\145', '\107', '\160', + '\040', '\145', '\162', '\040', '\061', '\012', '\165', '\120', + '\166', '\040', '\161', '\165', '\040', '\061', '\012', '\127', + '\161', '\145', '\040', '\161', '\165', '\040', '\061', '\012', + '\171', '\151', '\111', '\040', '\151', '\156', '\040', '\061', + '\012', '\162', '\161', '\106', '\040', '\161', '\165', '\040', + '\061', '\012', '\113', '\152', '\163', '\040', '\163', '\164', + '\040', '\061', '\012', '\154', '\167', '\113', '\040', '\154', + '\145', '\040', '\061', '\012', '\146', '\152', '\121', '\040', + '\151', '\152', '\040', '\061', '\012', '\165', '\111', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\144', '\170', + '\122', '\040', '\144', '\145', '\040', '\061', '\012', '\107', + '\161', '\152', '\040', '\161', '\165', '\040', '\061', '\012', + '\156', '\114', '\142', '\040', '\141', '\156', '\040', '\061', + '\012', '\147', '\122', '\144', '\040', '\156', '\147', '\040', + '\061', '\012', '\161', '\171', '\166', '\040', '\161', '\165', + '\040', '\061', '\012', '\167', '\164', '\132', '\040', '\164', + '\150', '\040', '\061', '\012', '\143', '\122', '\153', '\040', + '\143', '\150', '\040', '\061', '\012', '\151', '\113', '\146', + '\040', '\151', '\156', '\040', '\061', '\012', '\150', '\142', + '\113', '\040', '\164', '\150', '\040', '\061', '\012', '\162', + '\161', '\124', '\040', '\161', '\165', '\040', '\061', '\012', + '\170', '\155', '\106', '\040', '\155', '\145', '\040', '\061', + '\012', '\166', '\110', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\164', '\161', '\116', '\040', '\164', '\150', + '\040', '\061', '\012', '\166', '\114', '\166', '\040', '\166', + '\141', '\040', '\061', '\012', '\170', '\166', '\112', '\040', + '\166', '\141', '\040', '\061', '\012', '\142', '\147', '\112', + '\040', '\156', '\147', '\040', '\061', '\012', '\121', '\152', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\114', + '\166', '\142', '\040', '\166', '\141', '\040', '\061', '\012', + '\110', '\170', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\164', '\126', '\161', '\040', '\164', '\150', '\040', + '\061', '\012', '\162', '\150', '\132', '\040', '\164', '\150', + '\040', '\061', '\012', '\163', '\154', '\114', '\040', '\154', + '\145', '\040', '\061', '\012', '\153', '\144', '\110', '\040', + '\144', '\145', '\040', '\061', '\012', '\113', '\146', '\142', + '\040', '\142', '\145', '\040', '\061', '\012', '\104', '\146', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\103', + '\161', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\156', '\121', '\153', '\040', '\141', '\156', '\040', '\061', + '\012', '\127', '\156', '\172', '\040', '\141', '\156', '\040', + '\061', '\012', '\116', '\152', '\152', '\040', '\151', '\152', + '\040', '\061', '\012', '\142', '\112', '\146', '\040', '\142', + '\145', '\040', '\061', '\012', '\167', '\122', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\104', '\160', '\142', + '\040', '\160', '\162', '\040', '\061', '\012', '\163', '\120', + '\152', '\040', '\163', '\164', '\040', '\061', '\012', '\132', + '\160', '\156', '\040', '\141', '\156', '\040', '\061', '\012', + '\155', '\120', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\121', '\143', '\154', '\040', '\143', '\150', '\040', + '\061', '\012', '\172', '\103', '\144', '\040', '\163', '\172', + '\040', '\061', '\012', '\171', '\162', '\103', '\040', '\145', + '\162', '\040', '\061', '\012', '\150', '\103', '\142', '\040', + '\164', '\150', '\040', '\061', '\012', '\141', '\102', '\166', + '\040', '\141', '\156', '\040', '\061', '\012', '\171', '\165', + '\107', '\040', '\161', '\165', '\040', '\061', '\012', '\146', + '\143', '\116', '\040', '\143', '\150', '\040', '\061', '\012', + '\142', '\132', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\107', '\164', '\146', '\040', '\164', '\150', '\040', + '\061', '\012', '\167', '\142', '\127', '\040', '\167', '\141', + '\040', '\061', '\012', '\166', '\120', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\126', '\164', '\152', '\040', + '\164', '\150', '\040', '\061', '\012', '\153', '\127', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\112', '\142', + '\155', '\040', '\155', '\145', '\040', '\061', '\012', '\127', + '\155', '\142', '\040', '\155', '\145', '\040', '\061', '\012', + '\160', '\170', '\131', '\040', '\160', '\162', '\040', '\061', + '\012', '\150', '\121', '\170', '\040', '\164', '\150', '\040', + '\061', '\012', '\164', '\116', '\156', '\040', '\164', '\150', + '\040', '\061', '\012', '\161', '\144', '\170', '\040', '\161', + '\165', '\040', '\061', '\012', '\143', '\131', '\166', '\040', + '\143', '\150', '\040', '\061', '\012', '\172', '\154', '\130', + '\040', '\154', '\145', '\040', '\061', '\012', '\162', '\167', + '\106', '\040', '\145', '\162', '\040', '\061', '\012', '\143', + '\132', '\155', '\040', '\143', '\150', '\040', '\061', '\012', + '\171', '\142', '\112', '\040', '\142', '\145', '\040', '\061', + '\012', '\161', '\141', '\102', '\040', '\141', '\156', '\040', + '\061', '\012', '\164', '\126', '\152', '\040', '\164', '\150', + '\040', '\061', '\012', '\172', '\125', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\143', '\146', '\103', '\040', + '\143', '\150', '\040', '\061', '\012', '\150', '\170', '\102', + '\040', '\164', '\150', '\040', '\061', '\012', '\124', '\142', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\157', + '\106', '\156', '\040', '\141', '\156', '\040', '\061', '\012', + '\142', '\124', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\150', '\102', '\153', '\040', '\164', '\150', '\040', + '\061', '\012', '\150', '\121', '\145', '\040', '\164', '\150', + '\040', '\061', '\012', '\161', '\102', '\145', '\040', '\144', + '\145', '\040', '\061', '\012', '\144', '\160', '\103', '\040', + '\144', '\145', '\040', '\061', '\012', '\153', '\160', '\127', + '\040', '\153', '\141', '\040', '\061', '\012', '\132', '\153', + '\152', '\040', '\151', '\152', '\040', '\061', '\012', '\116', + '\167', '\156', '\040', '\141', '\156', '\040', '\061', '\012', + '\147', '\162', '\103', '\040', '\156', '\147', '\040', '\061', + '\012', '\165', '\130', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\125', '\157', '\171', '\040', '\157', '\156', + '\040', '\061', '\012', '\132', '\146', '\165', '\040', '\161', + '\165', '\040', '\061', '\012', '\170', '\113', '\142', '\040', + '\142', '\145', '\040', '\061', '\012', '\150', '\123', '\142', + '\040', '\164', '\150', '\040', '\061', '\012', '\142', '\120', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\161', + '\143', '\147', '\040', '\143', '\150', '\040', '\061', '\012', + '\170', '\111', '\165', '\040', '\161', '\165', '\040', '\061', + '\012', '\147', '\102', '\166', '\040', '\156', '\147', '\040', + '\061', '\012', '\147', '\132', '\155', '\040', '\155', '\145', + '\040', '\061', '\012', '\161', '\120', '\165', '\040', '\165', + '\156', '\040', '\061', '\012', '\102', '\146', '\160', '\040', + '\160', '\162', '\040', '\061', '\012', '\162', '\170', '\103', + '\040', '\145', '\162', '\040', '\061', '\012', '\163', '\114', + '\153', '\040', '\163', '\164', '\040', '\061', '\012', '\150', + '\107', '\152', '\040', '\164', '\150', '\040', '\061', '\012', + '\161', '\166', '\122', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\160', '\122', '\040', '\161', '\165', '\040', + '\061', '\012', '\166', '\116', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\104', '\146', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\156', '\122', '\161', '\040', + '\141', '\156', '\040', '\061', '\012', '\153', '\150', '\122', + '\040', '\164', '\150', '\040', '\061', '\012', '\160', '\161', + '\120', '\040', '\161', '\165', '\040', '\061', '\012', '\164', + '\116', '\160', '\040', '\164', '\150', '\040', '\061', '\012', + '\126', '\167', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\167', '\101', '\040', '\167', '\141', '\040', + '\061', '\012', '\167', '\115', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\123', '\156', '\161', '\040', '\141', + '\156', '\040', '\061', '\012', '\144', '\146', '\104', '\040', + '\144', '\145', '\040', '\061', '\012', '\166', '\107', '\167', + '\040', '\166', '\141', '\040', '\061', '\012', '\130', '\161', + '\142', '\040', '\161', '\165', '\040', '\061', '\012', '\113', + '\167', '\167', '\040', '\167', '\141', '\040', '\061', '\012', + '\121', '\150', '\170', '\040', '\164', '\150', '\040', '\061', + '\012', '\117', '\171', '\170', '\040', '\156', '\171', '\040', + '\061', '\012', '\144', '\166', '\102', '\040', '\144', '\145', + '\040', '\061', '\012', '\163', '\126', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\110', '\143', '\156', '\040', + '\143', '\150', '\040', '\061', '\012', '\163', '\142', '\125', + '\040', '\163', '\164', '\040', '\061', '\012', '\146', '\106', + '\167', '\040', '\167', '\141', '\040', '\061', '\012', '\153', + '\146', '\124', '\040', '\153', '\141', '\040', '\061', '\012', + '\162', '\166', '\127', '\040', '\145', '\162', '\040', '\061', + '\012', '\131', '\170', '\167', '\040', '\167', '\141', '\040', + '\061', '\012', '\156', '\106', '\153', '\040', '\141', '\156', + '\040', '\061', '\012', '\114', '\161', '\144', '\040', '\161', + '\165', '\040', '\061', '\012', '\150', '\157', '\121', '\040', + '\164', '\150', '\040', '\061', '\012', '\116', '\146', '\152', + '\040', '\151', '\152', '\040', '\061', '\012', '\147', '\162', + '\110', '\040', '\156', '\147', '\040', '\061', '\012', '\143', + '\112', '\153', '\040', '\143', '\150', '\040', '\061', '\012', + '\120', '\156', '\166', '\040', '\141', '\156', '\040', '\061', + '\012', '\116', '\161', '\170', '\040', '\161', '\165', '\040', + '\061', '\012', '\171', '\146', '\105', '\040', '\156', '\171', + '\040', '\061', '\012', '\153', '\155', '\111', '\040', '\153', + '\141', '\040', '\061', '\012', '\107', '\155', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\142', '\170', '\123', + '\040', '\142', '\145', '\040', '\061', '\012', '\161', '\165', + '\125', '\040', '\165', '\156', '\040', '\061', '\012', '\161', + '\131', '\146', '\040', '\161', '\165', '\040', '\061', '\012', + '\172', '\113', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\167', '\150', '\113', '\040', '\164', '\150', '\040', + '\061', '\012', '\157', '\146', '\131', '\040', '\157', '\156', + '\040', '\061', '\012', '\160', '\162', '\110', '\040', '\145', + '\162', '\040', '\061', '\012', '\152', '\130', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\166', '\121', '\155', + '\040', '\166', '\141', '\040', '\061', '\012', '\151', '\127', + '\170', '\040', '\151', '\156', '\040', '\061', '\012', '\142', + '\172', '\103', '\040', '\163', '\172', '\040', '\061', '\012', + '\156', '\131', '\170', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\141', '\113', '\040', '\141', '\156', '\040', + '\061', '\012', '\107', '\147', '\142', '\040', '\156', '\147', + '\040', '\061', '\012', '\172', '\123', '\146', '\040', '\163', + '\172', '\040', '\061', '\012', '\162', '\121', '\172', '\040', + '\145', '\162', '\040', '\061', '\012', '\150', '\153', '\127', + '\040', '\164', '\150', '\040', '\061', '\012', '\126', '\156', + '\154', '\040', '\141', '\156', '\040', '\061', '\012', '\107', + '\164', '\144', '\040', '\164', '\150', '\040', '\061', '\012', + '\162', '\115', '\167', '\040', '\145', '\162', '\040', '\061', + '\012', '\167', '\166', '\130', '\040', '\166', '\141', '\040', + '\061', '\012', '\152', '\171', '\125', '\040', '\151', '\152', + '\040', '\061', '\012', '\121', '\161', '\160', '\040', '\161', + '\165', '\040', '\061', '\012', '\110', '\156', '\161', '\040', + '\141', '\156', '\040', '\061', '\012', '\142', '\106', '\142', + '\040', '\142', '\145', '\040', '\061', '\012', '\161', '\153', + '\110', '\040', '\161', '\165', '\040', '\061', '\012', '\127', + '\143', '\153', '\040', '\143', '\150', '\040', '\061', '\012', + '\146', '\115', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\172', '\147', '\105', '\040', '\156', '\147', '\040', + '\061', '\012', '\157', '\112', '\172', '\040', '\157', '\156', + '\040', '\061', '\012', '\170', '\166', '\110', '\040', '\166', + '\141', '\040', '\061', '\012', '\150', '\121', '\171', '\040', + '\164', '\150', '\040', '\061', '\012', '\143', '\131', '\146', + '\040', '\143', '\150', '\040', '\061', '\012', '\143', '\170', + '\104', '\040', '\143', '\150', '\040', '\061', '\012', '\171', + '\104', '\163', '\040', '\163', '\164', '\040', '\061', '\012', + '\161', '\102', '\150', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\112', '\170', '\040', '\143', '\150', '\040', + '\061', '\012', '\144', '\120', '\152', '\040', '\144', '\145', + '\040', '\061', '\012', '\167', '\127', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\162', '\110', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\151', '\171', '\115', + '\040', '\151', '\156', '\040', '\061', '\012', '\171', '\170', + '\104', '\040', '\156', '\171', '\040', '\061', '\012', '\153', + '\120', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\143', '\130', '\166', '\040', '\143', '\150', '\040', '\061', + '\012', '\116', '\155', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\166', '\153', '\116', '\040', '\153', '\141', + '\040', '\061', '\012', '\154', '\106', '\152', '\040', '\154', + '\145', '\040', '\061', '\012', '\171', '\155', '\125', '\040', + '\155', '\145', '\040', '\061', '\012', '\160', '\132', '\166', + '\040', '\166', '\141', '\040', '\061', '\012', '\147', '\132', + '\164', '\040', '\164', '\150', '\040', '\061', '\012', '\112', + '\161', '\171', '\040', '\161', '\165', '\040', '\061', '\012', + '\161', '\101', '\172', '\040', '\161', '\165', '\040', '\061', + '\012', '\102', '\143', '\171', '\040', '\143', '\150', '\040', + '\061', '\012', '\160', '\161', '\152', '\040', '\161', '\165', + '\040', '\061', '\012', '\143', '\161', '\105', '\040', '\143', + '\150', '\040', '\061', '\012', '\122', '\167', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\143', '\162', '\115', + '\040', '\143', '\150', '\040', '\061', '\012', '\101', '\170', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\132', + '\152', '\160', '\040', '\151', '\152', '\040', '\061', '\012', + '\171', '\170', '\106', '\040', '\156', '\171', '\040', '\061', + '\012', '\166', '\132', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\163', '\120', '\142', '\040', '\163', '\164', + '\040', '\061', '\012', '\166', '\103', '\163', '\040', '\163', + '\164', '\040', '\061', '\012', '\146', '\121', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\161', '\131', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\150', '\102', + '\160', '\040', '\164', '\150', '\040', '\061', '\012', '\112', + '\142', '\153', '\040', '\153', '\141', '\040', '\061', '\012', + '\147', '\161', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\162', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\103', '\146', '\172', '\040', '\163', '\172', + '\040', '\061', '\012', '\155', '\142', '\112', '\040', '\155', + '\145', '\040', '\061', '\012', '\146', '\122', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\111', '\167', '\166', + '\040', '\166', '\141', '\040', '\061', '\012', '\165', '\106', + '\156', '\040', '\141', '\156', '\040', '\061', '\012', '\143', + '\131', '\172', '\040', '\143', '\150', '\040', '\061', '\012', + '\161', '\104', '\142', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\110', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\161', '\155', '\111', '\040', '\161', '\165', + '\040', '\061', '\012', '\171', '\143', '\105', '\040', '\143', + '\150', '\040', '\061', '\012', '\115', '\150', '\146', '\040', + '\164', '\150', '\040', '\061', '\012', '\151', '\165', '\105', + '\040', '\161', '\165', '\040', '\061', '\012', '\147', '\130', + '\146', '\040', '\156', '\147', '\040', '\061', '\012', '\154', + '\120', '\171', '\040', '\154', '\145', '\040', '\061', '\012', + '\142', '\120', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\152', '\130', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\147', '\117', '\170', '\040', '\156', '\147', + '\040', '\061', '\012', '\116', '\155', '\166', '\040', '\166', + '\141', '\040', '\061', '\012', '\170', '\104', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\103', '\167', '\144', + '\040', '\144', '\145', '\040', '\061', '\012', '\154', '\152', + '\120', '\040', '\154', '\145', '\040', '\061', '\012', '\167', + '\161', '\126', '\040', '\161', '\165', '\040', '\061', '\012', + '\156', '\162', '\105', '\040', '\141', '\156', '\040', '\061', + '\012', '\113', '\155', '\167', '\040', '\155', '\145', '\040', + '\061', '\012', '\147', '\112', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\164', '\147', '\102', '\040', '\164', + '\150', '\040', '\061', '\012', '\170', '\172', '\122', '\040', + '\163', '\172', '\040', '\061', '\012', '\166', '\112', '\162', + '\040', '\145', '\162', '\040', '\061', '\012', '\141', '\125', + '\151', '\040', '\141', '\156', '\040', '\061', '\012', '\171', + '\156', '\131', '\040', '\141', '\156', '\040', '\061', '\012', + '\142', '\132', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\146', '\106', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\123', '\170', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\161', '\101', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\151', '\132', '\166', '\040', + '\151', '\156', '\040', '\061', '\012', '\152', '\130', '\165', + '\040', '\161', '\165', '\040', '\061', '\012', '\147', '\160', + '\122', '\040', '\156', '\147', '\040', '\061', '\012', '\167', + '\126', '\154', '\040', '\154', '\145', '\040', '\061', '\012', + '\144', '\116', '\152', '\040', '\144', '\145', '\040', '\061', + '\012', '\146', '\102', '\167', '\040', '\167', '\141', '\040', + '\061', '\012', '\115', '\152', '\171', '\040', '\151', '\152', + '\040', '\061', '\012', '\153', '\152', '\132', '\040', '\151', + '\152', '\040', '\061', '\012', '\164', '\114', '\163', '\040', + '\164', '\150', '\040', '\061', '\012', '\151', '\131', '\152', + '\040', '\151', '\156', '\040', '\061', '\012', '\167', '\142', + '\117', '\040', '\167', '\141', '\040', '\061', '\012', '\161', + '\130', '\142', '\040', '\161', '\165', '\040', '\061', '\012', + '\165', '\112', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\113', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\166', '\152', '\117', '\040', '\151', '\152', + '\040', '\061', '\012', '\167', '\165', '\104', '\040', '\161', + '\165', '\040', '\061', '\012', '\142', '\154', '\121', '\040', + '\154', '\145', '\040', '\061', '\012', '\171', '\146', '\102', + '\040', '\156', '\171', '\040', '\061', '\012', '\121', '\163', + '\153', '\040', '\163', '\164', '\040', '\061', '\012', '\125', + '\167', '\155', '\040', '\155', '\145', '\040', '\061', '\012', + '\132', '\161', '\147', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\155', '\131', '\040', '\141', '\156', '\040', + '\061', '\012', '\160', '\130', '\167', '\040', '\160', '\162', + '\040', '\061', '\012', '\171', '\126', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\147', '\111', '\167', '\040', + '\156', '\147', '\040', '\061', '\012', '\110', '\170', '\153', + '\040', '\153', '\141', '\040', '\061', '\012', '\120', '\147', + '\171', '\040', '\156', '\147', '\040', '\061', '\012', '\154', + '\121', '\166', '\040', '\154', '\145', '\040', '\061', '\012', + '\142', '\156', '\113', '\040', '\141', '\156', '\040', '\061', + '\012', '\170', '\164', '\132', '\040', '\164', '\150', '\040', + '\061', '\012', '\121', '\143', '\145', '\040', '\143', '\150', + '\040', '\061', '\012', '\116', '\152', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\155', '\166', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\115', '\167', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\107', '\164', + '\156', '\040', '\164', '\150', '\040', '\061', '\012', '\146', + '\112', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\166', '\112', '\172', '\040', '\163', '\172', '\040', '\061', + '\012', '\147', '\104', '\153', '\040', '\156', '\147', '\040', + '\061', '\012', '\144', '\114', '\167', '\040', '\144', '\145', + '\040', '\061', '\012', '\157', '\145', '\125', '\040', '\145', + '\162', '\040', '\061', '\012', '\143', '\166', '\131', '\040', + '\143', '\150', '\040', '\061', '\012', '\107', '\142', '\142', + '\040', '\142', '\145', '\040', '\061', '\012', '\124', '\161', + '\144', '\040', '\161', '\165', '\040', '\061', '\012', '\141', + '\124', '\160', '\040', '\141', '\156', '\040', '\061', '\012', + '\131', '\167', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\152', '\144', '\124', '\040', '\144', '\145', '\040', + '\061', '\012', '\127', '\153', '\155', '\040', '\153', '\141', + '\040', '\061', '\012', '\160', '\170', '\101', '\040', '\160', + '\162', '\040', '\061', '\012', '\166', '\104', '\154', '\040', + '\154', '\145', '\040', '\061', '\012', '\163', '\146', '\104', + '\040', '\163', '\164', '\040', '\061', '\012', '\162', '\161', + '\126', '\040', '\161', '\165', '\040', '\061', '\012', '\143', + '\110', '\142', '\040', '\143', '\150', '\040', '\061', '\012', + '\151', '\126', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\115', '\146', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\163', '\126', '\155', '\040', '\163', '\164', + '\040', '\061', '\012', '\156', '\172', '\122', '\040', '\141', + '\156', '\040', '\061', '\012', '\121', '\166', '\163', '\040', + '\163', '\164', '\040', '\061', '\012', '\153', '\132', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\127', '\156', + '\167', '\040', '\141', '\156', '\040', '\061', '\012', '\161', + '\132', '\142', '\040', '\161', '\165', '\040', '\061', '\012', + '\107', '\166', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\120', '\153', '\040', '\153', '\141', '\040', + '\061', '\012', '\123', '\170', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\166', '\116', '\147', '\040', '\156', + '\147', '\040', '\061', '\012', '\161', '\162', '\110', '\040', + '\161', '\165', '\040', '\061', '\012', '\146', '\114', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\167', '\126', + '\163', '\040', '\163', '\164', '\040', '\061', '\012', '\161', + '\105', '\150', '\040', '\164', '\150', '\040', '\061', '\012', + '\165', '\161', '\103', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\132', '\170', '\040', '\164', '\150', '\040', + '\061', '\012', '\171', '\150', '\111', '\040', '\164', '\150', + '\040', '\061', '\012', '\167', '\116', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\162', '\106', '\152', '\040', + '\145', '\162', '\040', '\061', '\012', '\170', '\120', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\160', '\161', + '\127', '\040', '\161', '\165', '\040', '\061', '\012', '\120', + '\152', '\143', '\040', '\143', '\150', '\040', '\061', '\012', + '\152', '\131', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\160', '\106', '\166', '\040', '\166', '\141', '\040', + '\061', '\012', '\166', '\114', '\162', '\040', '\145', '\162', + '\040', '\061', '\012', '\154', '\161', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\170', '\112', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\154', '\126', '\172', + '\040', '\154', '\145', '\040', '\061', '\012', '\143', '\132', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\150', + '\143', '\106', '\040', '\164', '\150', '\040', '\061', '\012', + '\165', '\150', '\112', '\040', '\164', '\150', '\040', '\061', + '\012', '\143', '\114', '\152', '\040', '\143', '\150', '\040', + '\061', '\012', '\161', '\171', '\127', '\040', '\161', '\165', + '\040', '\061', '\012', '\172', '\150', '\124', '\040', '\164', + '\150', '\040', '\061', '\012', '\155', '\164', '\113', '\040', + '\164', '\150', '\040', '\061', '\012', '\160', '\122', '\142', + '\040', '\160', '\162', '\040', '\061', '\012', '\142', '\103', + '\170', '\040', '\142', '\145', '\040', '\061', '\012', '\156', + '\112', '\146', '\040', '\141', '\156', '\040', '\061', '\012', + '\152', '\167', '\106', '\040', '\151', '\152', '\040', '\061', + '\012', '\120', '\144', '\152', '\040', '\144', '\145', '\040', + '\061', '\012', '\152', '\170', '\105', '\040', '\151', '\152', + '\040', '\061', '\012', '\163', '\154', '\132', '\040', '\154', + '\145', '\040', '\061', '\012', '\114', '\170', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\172', '\156', '\114', + '\040', '\141', '\156', '\040', '\061', '\012', '\155', '\172', + '\126', '\040', '\163', '\172', '\040', '\061', '\012', '\154', + '\107', '\161', '\040', '\154', '\145', '\040', '\061', '\012', + '\121', '\142', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\152', '\142', '\131', '\040', '\151', '\152', '\040', + '\061', '\012', '\172', '\123', '\155', '\040', '\163', '\172', + '\040', '\061', '\012', '\121', '\161', '\170', '\040', '\161', + '\165', '\040', '\061', '\012', '\171', '\160', '\122', '\040', + '\160', '\162', '\040', '\061', '\012', '\147', '\103', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\131', '\166', + '\170', '\040', '\166', '\141', '\040', '\061', '\012', '\151', + '\150', '\111', '\040', '\164', '\150', '\040', '\061', '\012', + '\132', '\146', '\170', '\040', '\146', '\157', '\040', '\061', + '\012', '\156', '\152', '\111', '\040', '\156', '\144', '\040', + '\061', '\012', '\131', '\160', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\154', '\170', '\124', '\040', '\154', + '\145', '\040', '\061', '\012', '\146', '\126', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\112', '\172', '\155', + '\040', '\163', '\172', '\040', '\061', '\012', '\152', '\170', + '\101', '\040', '\151', '\152', '\040', '\061', '\012', '\147', + '\104', '\154', '\040', '\156', '\147', '\040', '\061', '\012', + '\105', '\141', '\161', '\040', '\141', '\156', '\040', '\061', + '\012', '\121', '\143', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\172', '\107', '\142', '\040', '\163', '\172', + '\040', '\061', '\012', '\152', '\114', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\161', '\153', '\130', '\040', + '\161', '\165', '\040', '\061', '\012', '\167', '\142', '\113', + '\040', '\167', '\141', '\040', '\061', '\012', '\156', '\116', + '\170', '\040', '\141', '\156', '\040', '\061', '\012', '\163', + '\161', '\127', '\040', '\161', '\165', '\040', '\061', '\012', + '\167', '\122', '\170', '\040', '\167', '\141', '\040', '\061', + '\012', '\170', '\162', '\125', '\040', '\145', '\162', '\040', + '\061', '\012', '\146', '\156', '\121', '\040', '\141', '\156', + '\040', '\061', '\012', '\153', '\172', '\102', '\040', '\163', + '\172', '\040', '\061', '\012', '\122', '\143', '\156', '\040', + '\143', '\150', '\040', '\061', '\012', '\161', '\142', '\114', + '\040', '\161', '\165', '\040', '\061', '\012', '\163', '\162', + '\104', '\040', '\145', '\162', '\040', '\061', '\012', '\126', + '\170', '\165', '\040', '\161', '\165', '\040', '\061', '\012', + '\161', '\166', '\106', '\040', '\161', '\165', '\040', '\061', + '\012', '\167', '\112', '\162', '\040', '\145', '\162', '\040', + '\061', '\012', '\131', '\170', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\161', '\151', '\131', '\040', '\161', + '\165', '\040', '\061', '\012', '\146', '\115', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\150', '\142', '\131', + '\040', '\164', '\150', '\040', '\061', '\012', '\150', '\147', + '\110', '\040', '\164', '\150', '\040', '\061', '\012', '\144', + '\155', '\123', '\040', '\144', '\145', '\040', '\061', '\012', + '\152', '\124', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\132', '\152', '\155', '\040', '\151', '\152', '\040', + '\061', '\012', '\116', '\152', '\154', '\040', '\154', '\145', + '\040', '\061', '\012', '\144', '\161', '\126', '\040', '\161', + '\165', '\040', '\061', '\012', '\131', '\152', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\162', '\113', '\167', + '\040', '\145', '\162', '\040', '\061', '\012', '\143', '\170', + '\125', '\040', '\143', '\150', '\040', '\061', '\012', '\103', + '\153', '\152', '\040', '\151', '\152', '\040', '\061', '\012', + '\172', '\146', '\112', '\040', '\163', '\172', '\040', '\061', + '\012', '\171', '\164', '\106', '\040', '\164', '\150', '\040', + '\061', '\012', '\170', '\162', '\120', '\040', '\145', '\162', + '\040', '\061', '\012', '\161', '\105', '\152', '\040', '\161', + '\165', '\040', '\061', '\012', '\162', '\170', '\117', '\040', + '\145', '\162', '\040', '\061', '\012', '\162', '\132', '\156', + '\040', '\141', '\156', '\040', '\061', '\012', '\142', '\132', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\143', + '\130', '\161', '\040', '\143', '\150', '\040', '\061', '\012', + '\167', '\166', '\104', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\143', '\130', '\040', '\164', '\150', '\040', + '\061', '\012', '\172', '\153', '\117', '\040', '\163', '\172', + '\040', '\061', '\012', '\150', '\116', '\170', '\040', '\164', + '\150', '\040', '\061', '\012', '\167', '\106', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\153', '\130', '\165', + '\040', '\161', '\165', '\040', '\061', '\012', '\126', '\153', + '\156', '\040', '\141', '\156', '\040', '\061', '\012', '\107', + '\152', '\172', '\040', '\163', '\172', '\040', '\061', '\012', + '\121', '\143', '\144', '\040', '\143', '\150', '\040', '\061', + '\012', '\171', '\166', '\106', '\040', '\166', '\141', '\040', + '\061', '\012', '\170', '\106', '\170', '\040', '\170', '\145', + '\040', '\061', '\012', '\144', '\123', '\152', '\040', '\144', + '\145', '\040', '\061', '\012', '\170', '\120', '\142', '\040', + '\142', '\145', '\040', '\061', '\012', '\157', '\106', '\160', + '\040', '\157', '\156', '\040', '\061', '\012', '\161', '\101', + '\153', '\040', '\161', '\165', '\040', '\061', '\012', '\162', + '\161', '\125', '\040', '\161', '\165', '\040', '\061', '\012', + '\160', '\107', '\166', '\040', '\166', '\141', '\040', '\061', + '\012', '\150', '\172', '\103', '\040', '\164', '\150', '\040', + '\061', '\012', '\161', '\111', '\153', '\040', '\161', '\165', + '\040', '\061', '\012', '\114', '\150', '\154', '\040', '\164', + '\150', '\040', '\061', '\012', '\106', '\167', '\142', '\040', + '\167', '\141', '\040', '\061', '\012', '\160', '\147', '\105', + '\040', '\156', '\147', '\040', '\061', '\012', '\101', '\167', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\146', + '\102', '\153', '\040', '\153', '\141', '\040', '\061', '\012', + '\170', '\113', '\144', '\040', '\144', '\145', '\040', '\061', + '\012', '\120', '\146', '\167', '\040', '\167', '\141', '\040', + '\061', '\012', '\165', '\161', '\113', '\040', '\161', '\165', + '\040', '\061', '\012', '\160', '\112', '\143', '\040', '\143', + '\150', '\040', '\061', '\012', '\142', '\124', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\164', '\127', '\147', + '\040', '\164', '\150', '\040', '\061', '\012', '\147', '\144', + '\116', '\040', '\156', '\147', '\040', '\061', '\012', '\152', + '\162', '\116', '\040', '\145', '\162', '\040', '\061', '\012', + '\153', '\154', '\123', '\040', '\154', '\145', '\040', '\061', + '\012', '\161', '\105', '\151', '\040', '\161', '\165', '\040', + '\061', '\012', '\163', '\106', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\164', '\161', '\122', '\040', '\164', + '\150', '\040', '\061', '\012', '\106', '\156', '\155', '\040', + '\141', '\156', '\040', '\061', '\012', '\150', '\130', '\166', + '\040', '\164', '\150', '\040', '\061', '\012', '\146', '\170', + '\116', '\040', '\146', '\157', '\040', '\061', '\012', '\142', + '\166', '\114', '\040', '\166', '\141', '\040', '\061', '\012', + '\157', '\107', '\146', '\040', '\157', '\156', '\040', '\061', + '\012', '\150', '\132', '\155', '\040', '\164', '\150', '\040', + '\061', '\012', '\171', '\146', '\110', '\040', '\156', '\171', + '\040', '\061', '\012', '\144', '\143', '\105', '\040', '\143', + '\150', '\040', '\061', '\012', '\160', '\147', '\127', '\040', + '\156', '\147', '\040', '\061', '\012', '\167', '\162', '\102', + '\040', '\145', '\162', '\040', '\061', '\012', '\153', '\127', + '\155', '\040', '\153', '\141', '\040', '\061', '\012', '\123', + '\150', '\170', '\040', '\164', '\150', '\040', '\061', '\012', + '\164', '\167', '\120', '\040', '\164', '\150', '\040', '\061', + '\012', '\121', '\166', '\144', '\040', '\144', '\145', '\040', + '\061', '\012', '\121', '\147', '\165', '\040', '\161', '\165', + '\040', '\061', '\012', '\160', '\112', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\172', '\116', '\166', '\040', + '\163', '\172', '\040', '\061', '\012', '\110', '\160', '\150', + '\040', '\164', '\150', '\040', '\061', '\012', '\153', '\154', + '\106', '\040', '\154', '\145', '\040', '\061', '\012', '\166', + '\161', '\172', '\040', '\161', '\165', '\040', '\061', '\012', + '\163', '\147', '\107', '\040', '\156', '\147', '\040', '\061', + '\012', '\153', '\144', '\132', '\040', '\144', '\145', '\040', + '\061', '\012', '\145', '\152', '\130', '\040', '\145', '\162', + '\040', '\061', '\012', '\120', '\170', '\165', '\040', '\161', + '\165', '\040', '\061', '\012', '\160', '\166', '\124', '\040', + '\166', '\141', '\040', '\061', '\012', '\113', '\161', '\170', + '\040', '\161', '\165', '\040', '\061', '\012', '\121', '\155', + '\142', '\040', '\155', '\145', '\040', '\061', '\012', '\170', + '\106', '\153', '\040', '\153', '\141', '\040', '\061', '\012', + '\167', '\121', '\142', '\040', '\167', '\141', '\040', '\061', + '\012', '\120', '\147', '\170', '\040', '\156', '\147', '\040', + '\061', '\012', '\171', '\160', '\114', '\040', '\160', '\162', + '\040', '\061', '\012', '\142', '\167', '\105', '\040', '\167', + '\141', '\040', '\061', '\012', '\170', '\110', '\164', '\040', + '\164', '\150', '\040', '\061', '\012', '\153', '\126', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\152', '\155', + '\106', '\040', '\151', '\152', '\040', '\061', '\012', '\111', + '\170', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\161', '\171', '\120', '\040', '\161', '\165', '\040', '\061', + '\012', '\162', '\126', '\166', '\040', '\145', '\162', '\040', + '\061', '\012', '\131', '\164', '\167', '\040', '\164', '\150', + '\040', '\061', '\012', '\161', '\160', '\132', '\040', '\161', + '\165', '\040', '\061', '\012', '\164', '\160', '\132', '\040', + '\164', '\150', '\040', '\061', '\012', '\172', '\152', '\130', + '\040', '\163', '\172', '\040', '\061', '\012', '\113', '\150', + '\147', '\040', '\164', '\150', '\040', '\061', '\012', '\161', + '\146', '\126', '\040', '\161', '\165', '\040', '\061', '\012', + '\112', '\172', '\170', '\040', '\163', '\172', '\040', '\061', + '\012', '\153', '\124', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\102', '\172', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\156', '\152', '\122', '\040', '\141', + '\156', '\040', '\061', '\012', '\143', '\147', '\127', '\040', + '\143', '\150', '\040', '\061', '\012', '\143', '\155', '\111', + '\040', '\143', '\150', '\040', '\061', '\012', '\153', '\103', + '\142', '\040', '\153', '\141', '\040', '\061', '\012', '\160', + '\131', '\160', '\040', '\160', '\162', '\040', '\061', '\012', + '\166', '\153', '\132', '\040', '\153', '\141', '\040', '\061', + '\012', '\167', '\166', '\153', '\040', '\153', '\141', '\040', + '\061', '\012', '\126', '\146', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\156', '\154', '\132', '\040', '\141', + '\156', '\040', '\061', '\012', '\161', '\116', '\152', '\040', + '\161', '\165', '\040', '\061', '\012', '\162', '\103', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\153', '\142', + '\126', '\040', '\153', '\141', '\040', '\061', '\012', '\104', + '\161', '\152', '\040', '\161', '\165', '\040', '\061', '\012', + '\142', '\162', '\104', '\040', '\145', '\162', '\040', '\061', + '\012', '\154', '\142', '\107', '\040', '\154', '\145', '\040', + '\061', '\012', '\170', '\150', '\106', '\040', '\164', '\150', + '\040', '\061', '\012', '\153', '\170', '\132', '\040', '\153', + '\141', '\040', '\061', '\012', '\111', '\165', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\171', '\106', '\170', + '\040', '\156', '\171', '\040', '\061', '\012', '\161', '\126', + '\154', '\040', '\161', '\165', '\040', '\061', '\012', '\154', + '\143', '\107', '\040', '\143', '\150', '\040', '\061', '\012', + '\166', '\127', '\162', '\040', '\145', '\162', '\040', '\061', + '\012', '\141', '\102', '\161', '\040', '\141', '\156', '\040', + '\061', '\012', '\171', '\112', '\153', '\040', '\153', '\141', + '\040', '\061', '\012', '\143', '\172', '\114', '\040', '\143', + '\150', '\040', '\061', '\012', '\152', '\111', '\165', '\040', + '\161', '\165', '\040', '\061', '\012', '\166', '\125', '\154', + '\040', '\154', '\145', '\040', '\061', '\012', '\160', '\132', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\166', + '\164', '\127', '\040', '\164', '\150', '\040', '\061', '\012', + '\121', '\170', '\167', '\040', '\167', '\141', '\040', '\061', + '\012', '\144', '\131', '\166', '\040', '\144', '\145', '\040', + '\061', '\012', '\151', '\161', '\110', '\040', '\161', '\165', + '\040', '\061', '\012', '\130', '\167', '\163', '\040', '\163', + '\164', '\040', '\061', '\012', '\146', '\104', '\152', '\040', + '\151', '\152', '\040', '\061', '\012', '\170', '\126', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\144', '\113', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\166', + '\146', '\121', '\040', '\166', '\141', '\040', '\061', '\012', + '\150', '\166', '\104', '\040', '\164', '\150', '\040', '\061', + '\012', '\167', '\144', '\131', '\040', '\144', '\145', '\040', + '\061', '\012', '\110', '\172', '\172', '\040', '\163', '\172', + '\040', '\061', '\012', '\143', '\131', '\163', '\040', '\143', + '\150', '\040', '\061', '\012', '\106', '\164', '\152', '\040', + '\164', '\150', '\040', '\061', '\012', '\144', '\160', '\125', + '\040', '\144', '\145', '\040', '\061', '\012', '\114', '\154', + '\144', '\040', '\154', '\145', '\040', '\061', '\012', '\107', + '\161', '\167', '\040', '\161', '\165', '\040', '\061', '\012', + '\153', '\144', '\122', '\040', '\144', '\145', '\040', '\061', + '\012', '\166', '\130', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\161', '\163', '\131', '\040', '\161', '\165', + '\040', '\061', '\012', '\152', '\116', '\146', '\040', '\151', + '\152', '\040', '\061', '\012', '\121', '\152', '\152', '\040', + '\151', '\152', '\040', '\061', '\012', '\160', '\126', '\154', + '\040', '\154', '\145', '\040', '\061', '\012', '\112', '\155', + '\170', '\040', '\155', '\145', '\040', '\061', '\012', '\160', + '\104', '\152', '\040', '\151', '\152', '\040', '\061', '\012', + '\151', '\102', '\143', '\040', '\143', '\150', '\040', '\061', + '\012', '\153', '\114', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\170', '\156', '\107', '\040', '\141', '\156', + '\040', '\061', '\012', '\166', '\124', '\154', '\040', '\154', + '\145', '\040', '\061', '\012', '\116', '\144', '\147', '\040', + '\156', '\147', '\040', '\061', '\012', '\160', '\161', '\125', + '\040', '\161', '\165', '\040', '\061', '\012', '\125', '\141', + '\167', '\040', '\141', '\156', '\040', '\061', '\012', '\146', + '\172', '\116', '\040', '\163', '\172', '\040', '\061', '\012', + '\147', '\116', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\153', '\152', '\115', '\040', '\151', '\152', '\040', + '\061', '\012', '\154', '\156', '\113', '\040', '\141', '\156', + '\040', '\061', '\012', '\172', '\170', '\142', '\040', '\163', + '\172', '\040', '\061', '\012', '\153', '\143', '\123', '\040', + '\143', '\150', '\040', '\061', '\012', '\156', '\152', '\115', + '\040', '\141', '\156', '\040', '\061', '\012', '\107', '\144', + '\167', '\040', '\144', '\145', '\040', '\061', '\012', '\154', + '\156', '\132', '\040', '\141', '\156', '\040', '\061', '\012', + '\131', '\147', '\152', '\040', '\156', '\147', '\040', '\061', + '\012', '\150', '\113', '\144', '\040', '\164', '\150', '\040', + '\061', '\012', '\147', '\160', '\124', '\040', '\156', '\147', + '\040', '\061', '\012', '\171', '\161', '\120', '\040', '\161', + '\165', '\040', '\061', '\012', '\151', '\152', '\130', '\040', + '\151', '\156', '\040', '\061', '\012', '\152', '\107', '\146', + '\040', '\151', '\152', '\040', '\061', '\012', '\142', '\170', + '\111', '\040', '\142', '\145', '\040', '\061', '\012', '\166', + '\130', '\170', '\040', '\166', '\141', '\040', '\061', '\012', + '\126', '\162', '\167', '\040', '\145', '\162', '\040', '\061', + '\012', '\103', '\167', '\170', '\040', '\167', '\141', '\040', + '\061', '\012', '\156', '\102', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\161', '\166', '\171', '\040', '\161', + '\165', '\040', '\061', '\012', '\163', '\170', '\102', '\040', + '\163', '\164', '\040', '\061', '\012', '\155', '\126', '\153', + '\040', '\153', '\141', '\040', '\061', '\012', '\103', '\172', + '\170', '\040', '\163', '\172', '\040', '\061', '\012', '\146', + '\171', '\126', '\040', '\156', '\171', '\040', '\061', '\012', + '\143', '\130', '\167', '\040', '\143', '\150', '\040', '\061', + '\012', '\121', '\156', '\146', '\040', '\141', '\156', '\040', + '\061', '\012', '\131', '\161', '\144', '\040', '\161', '\165', + '\040', '\061', '\012', '\154', '\161', '\110', '\040', '\161', + '\165', '\040', '\061', '\012', '\144', '\142', '\131', '\040', + '\144', '\145', '\040', '\061', '\012', '\123', '\161', '\142', + '\040', '\161', '\165', '\040', '\061', '\012', '\113', '\161', + '\167', '\040', '\161', '\165', '\040', '\061', '\012', '\172', + '\160', '\112', '\040', '\163', '\172', '\040', '\061', '\012', + '\143', '\142', '\115', '\040', '\143', '\150', '\040', '\061', + '\012', '\172', '\106', '\147', '\040', '\156', '\147', '\040', + '\061', '\012', '\163', '\113', '\142', '\040', '\163', '\164', + '\040', '\061', '\012', '\161', '\162', '\113', '\040', '\161', + '\165', '\040', '\061', '\012', '\172', '\112', '\143', '\040', + '\143', '\150', '\040', '\061', '\012', '\156', '\122', '\156', + '\040', '\141', '\156', '\040', '\061', '\012', '\146', '\161', + '\116', '\040', '\161', '\165', '\040', '\061', '\012', '\150', + '\146', '\101', '\040', '\164', '\150', '\040', '\061', '\012', + '\161', '\157', '\107', '\040', '\161', '\165', '\040', '\061', + '\012', '\117', '\167', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\156', '\154', '\107', '\040', '\141', '\156', + '\040', '\061', '\012', '\167', '\111', '\170', '\040', '\167', + '\141', '\040', '\061', '\012', '\161', '\162', '\120', '\040', + '\161', '\165', '\040', '\061', '\012', '\116', '\167', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\161', '\141', + '\127', '\040', '\141', '\156', '\040', '\061', '\012', '\150', + '\143', '\124', '\040', '\164', '\150', '\040', '\061', '\012', + '\167', '\153', '\102', '\040', '\153', '\141', '\040', '\061', + '\012', '\116', '\144', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\113', '\172', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\147', '\170', '\102', '\040', '\156', + '\147', '\040', '\061', '\012', '\102', '\152', '\172', '\040', + '\163', '\172', '\040', '\061', '\012', '\166', '\124', '\146', + '\040', '\166', '\141', '\040', '\061', '\012', '\152', '\106', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\161', + '\115', '\145', '\040', '\161', '\165', '\040', '\061', '\012', + '\165', '\146', '\121', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\160', '\107', '\040', '\141', '\156', '\040', + '\061', '\012', '\165', '\132', '\153', '\040', '\161', '\165', + '\040', '\061', '\012', '\161', '\124', '\167', '\040', '\161', + '\165', '\040', '\061', '\012', '\107', '\154', '\167', '\040', + '\154', '\145', '\040', '\061', '\012', '\113', '\161', '\161', + '\040', '\161', '\165', '\040', '\061', '\012', '\103', '\170', + '\162', '\040', '\145', '\162', '\040', '\061', '\012', '\152', + '\132', '\163', '\040', '\163', '\164', '\040', '\061', '\012', + '\123', '\161', '\166', '\040', '\161', '\165', '\040', '\061', + '\012', '\171', '\120', '\155', '\040', '\155', '\145', '\040', + '\061', '\012', '\145', '\121', '\152', '\040', '\145', '\162', + '\040', '\061', '\012', '\141', '\111', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\147', '\104', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\154', '\111', '\160', + '\040', '\154', '\145', '\040', '\061', '\012', '\152', '\116', + '\152', '\040', '\151', '\152', '\040', '\061', '\012', '\161', + '\117', '\144', '\040', '\161', '\165', '\040', '\061', '\012', + '\166', '\153', '\115', '\040', '\153', '\141', '\040', '\061', + '\012', '\166', '\106', '\171', '\040', '\166', '\141', '\040', + '\061', '\012', '\143', '\146', '\126', '\040', '\143', '\150', + '\040', '\061', '\012', '\113', '\152', '\150', '\040', '\164', + '\150', '\040', '\061', '\012', '\147', '\153', '\120', '\040', + '\156', '\147', '\040', '\061', '\012', '\162', '\112', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\165', '\120', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\157', + '\172', '\121', '\040', '\157', '\156', '\040', '\061', '\012', + '\104', '\154', '\153', '\040', '\154', '\145', '\040', '\061', + '\012', '\166', '\130', '\150', '\040', '\164', '\150', '\040', + '\061', '\012', '\153', '\164', '\131', '\040', '\164', '\150', + '\040', '\061', '\012', '\166', '\127', '\171', '\040', '\166', + '\141', '\040', '\061', '\012', '\147', '\121', '\166', '\040', + '\156', '\147', '\040', '\061', '\012', '\131', '\167', '\167', + '\040', '\167', '\141', '\040', '\061', '\012', '\124', '\160', + '\172', '\040', '\163', '\172', '\040', '\061', '\012', '\121', + '\150', '\143', '\040', '\164', '\150', '\040', '\061', '\012', + '\170', '\165', '\124', '\040', '\161', '\165', '\040', '\061', + '\012', '\156', '\142', '\123', '\040', '\141', '\156', '\040', + '\061', '\012', '\172', '\121', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\166', '\147', '\132', '\040', '\156', + '\147', '\040', '\061', '\012', '\160', '\125', '\157', '\040', + '\157', '\156', '\040', '\061', '\012', '\165', '\127', '\142', + '\040', '\161', '\165', '\040', '\061', '\012', '\155', '\115', + '\146', '\040', '\155', '\145', '\040', '\061', '\012', '\132', + '\143', '\144', '\040', '\143', '\150', '\040', '\061', '\012', + '\151', '\102', '\160', '\040', '\151', '\156', '\040', '\061', + '\012', '\146', '\167', '\160', '\040', '\160', '\162', '\040', + '\061', '\012', '\172', '\131', '\146', '\040', '\163', '\172', + '\040', '\061', '\012', '\167', '\103', '\160', '\040', '\160', + '\162', '\040', '\061', '\012', '\103', '\161', '\171', '\040', + '\161', '\165', '\040', '\061', '\012', '\143', '\152', '\106', + '\040', '\143', '\150', '\040', '\061', '\012', '\107', '\146', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\155', + '\143', '\127', '\040', '\143', '\150', '\040', '\061', '\012', + '\143', '\161', '\126', '\040', '\143', '\150', '\040', '\061', + '\012', '\165', '\112', '\144', '\040', '\161', '\165', '\040', + '\061', '\012', '\151', '\125', '\152', '\040', '\151', '\156', + '\040', '\061', '\012', '\166', '\153', '\122', '\040', '\153', + '\141', '\040', '\061', '\012', '\167', '\147', '\111', '\040', + '\156', '\147', '\040', '\061', '\012', '\166', '\125', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\127', '\144', + '\156', '\040', '\144', '\145', '\040', '\061', '\012', '\163', + '\152', '\106', '\040', '\163', '\164', '\040', '\061', '\012', + '\164', '\120', '\166', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\122', '\156', '\040', '\141', '\156', '\040', + '\061', '\012', '\153', '\154', '\126', '\040', '\154', '\145', + '\040', '\061', '\012', '\163', '\142', '\115', '\040', '\163', + '\164', '\040', '\061', '\012', '\155', '\146', '\124', '\040', + '\155', '\145', '\040', '\061', '\012', '\144', '\142', '\126', + '\040', '\144', '\145', '\040', '\061', '\012', '\106', '\155', + '\156', '\040', '\141', '\156', '\040', '\061', '\012', '\147', + '\146', '\125', '\040', '\156', '\147', '\040', '\061', '\012', + '\143', '\142', '\102', '\040', '\143', '\150', '\040', '\061', + '\012', '\131', '\170', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\113', '\170', '\153', '\040', '\153', '\141', + '\040', '\061', '\012', '\104', '\167', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\167', '\147', '\130', '\040', + '\156', '\147', '\040', '\061', '\012', '\163', '\120', '\166', + '\040', '\163', '\164', '\040', '\061', '\012', '\166', '\110', + '\144', '\040', '\144', '\145', '\040', '\061', '\012', '\156', + '\142', '\110', '\040', '\141', '\156', '\040', '\061', '\012', + '\143', '\106', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\161', '\130', '\040', '\161', '\165', '\040', + '\061', '\012', '\152', '\106', '\145', '\040', '\145', '\162', + '\040', '\061', '\012', '\161', '\105', '\142', '\040', '\161', + '\165', '\040', '\061', '\012', '\144', '\106', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\165', '\105', '\157', + '\040', '\161', '\165', '\040', '\061', '\012', '\154', '\143', + '\111', '\040', '\143', '\150', '\040', '\061', '\012', '\142', + '\115', '\155', '\040', '\155', '\145', '\040', '\061', '\012', + '\172', '\132', '\167', '\040', '\163', '\172', '\040', '\061', + '\012', '\150', '\152', '\117', '\040', '\164', '\150', '\040', + '\061', '\012', '\150', '\113', '\170', '\040', '\164', '\150', + '\040', '\061', '\012', '\152', '\147', '\103', '\040', '\156', + '\147', '\040', '\061', '\012', '\143', '\156', '\114', '\040', + '\141', '\156', '\040', '\061', '\012', '\106', '\144', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\142', '\107', + '\146', '\040', '\142', '\145', '\040', '\061', '\012', '\123', + '\152', '\172', '\040', '\163', '\172', '\040', '\061', '\012', + '\142', '\115', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\130', '\167', '\040', '\166', '\141', '\040', + '\061', '\012', '\107', '\146', '\146', '\040', '\146', '\157', + '\040', '\061', '\012', '\103', '\167', '\167', '\040', '\167', + '\141', '\040', '\061', '\012', '\152', '\163', '\121', '\040', + '\163', '\164', '\040', '\061', '\012', '\132', '\147', '\166', + '\040', '\156', '\147', '\040', '\061', '\012', '\154', '\120', + '\146', '\040', '\154', '\145', '\040', '\061', '\012', '\156', + '\155', '\121', '\040', '\141', '\156', '\040', '\061', '\012', + '\126', '\144', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\143', '\130', '\040', '\143', '\150', '\040', + '\061', '\012', '\147', '\152', '\124', '\040', '\156', '\147', + '\040', '\061', '\012', '\155', '\167', '\105', '\040', '\155', + '\145', '\040', '\061', '\012', '\161', '\114', '\155', '\040', + '\161', '\165', '\040', '\061', '\012', '\143', '\110', '\161', + '\040', '\143', '\150', '\040', '\061', '\012', '\130', '\164', + '\156', '\040', '\164', '\150', '\040', '\061', '\012', '\116', + '\164', '\161', '\040', '\164', '\150', '\040', '\061', '\012', + '\147', '\127', '\153', '\040', '\156', '\147', '\040', '\061', + '\012', '\120', '\161', '\144', '\040', '\161', '\165', '\040', + '\061', '\012', '\161', '\160', '\120', '\040', '\161', '\165', + '\040', '\061', '\012', '\163', '\122', '\146', '\040', '\163', + '\164', '\040', '\061', '\012', '\161', '\160', '\114', '\040', + '\161', '\165', '\040', '\061', '\012', '\143', '\156', '\104', + '\040', '\141', '\156', '\040', '\061', '\012', '\161', '\160', + '\107', '\040', '\161', '\165', '\040', '\061', '\012', '\144', + '\172', '\123', '\040', '\163', '\172', '\040', '\061', '\012', + '\164', '\132', '\142', '\040', '\164', '\150', '\040', '\061', + '\012', '\171', '\147', '\115', '\040', '\156', '\147', '\040', + '\061', '\012', '\142', '\170', '\103', '\040', '\142', '\145', + '\040', '\061', '\012', '\144', '\146', '\125', '\040', '\144', + '\145', '\040', '\061', '\012', '\142', '\155', '\102', '\040', + '\155', '\145', '\040', '\061', '\012', '\154', '\102', '\172', + '\040', '\154', '\145', '\040', '\061', '\012', '\147', '\112', + '\170', '\040', '\156', '\147', '\040', '\061', '\012', '\131', + '\153', '\166', '\040', '\153', '\141', '\040', '\061', '\012', + '\132', '\144', '\153', '\040', '\144', '\145', '\040', '\061', + '\012', '\167', '\156', '\121', '\040', '\141', '\156', '\040', + '\061', '\012', '\164', '\132', '\152', '\040', '\164', '\150', + '\040', '\061', '\012', '\132', '\172', '\155', '\040', '\163', + '\172', '\040', '\061', '\012', '\126', '\146', '\150', '\040', + '\164', '\150', '\040', '\061', '\012', '\115', '\167', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\162', '\125', + '\157', '\040', '\157', '\156', '\040', '\061', '\012', '\161', + '\167', '\160', '\040', '\161', '\165', '\040', '\061', '\012', + '\164', '\143', '\111', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\146', '\104', '\040', '\164', '\150', '\040', + '\061', '\012', '\165', '\157', '\132', '\040', '\161', '\165', + '\040', '\061', '\012', '\146', '\103', '\167', '\040', '\167', + '\141', '\040', '\061', '\012', '\151', '\121', '\161', '\040', + '\161', '\165', '\040', '\061', '\012', '\161', '\102', '\147', + '\040', '\161', '\165', '\040', '\061', '\012', '\163', '\126', + '\142', '\040', '\163', '\164', '\040', '\061', '\012', '\160', + '\152', '\125', '\040', '\151', '\152', '\040', '\061', '\012', + '\163', '\143', '\121', '\040', '\143', '\150', '\040', '\061', + '\012', '\160', '\161', '\121', '\040', '\161', '\165', '\040', + '\061', '\012', '\163', '\166', '\132', '\040', '\163', '\164', + '\040', '\061', '\012', '\132', '\160', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\160', '\151', '\126', '\040', + '\151', '\156', '\040', '\061', '\012', '\153', '\142', '\120', + '\040', '\153', '\141', '\040', '\061', '\012', '\167', '\161', + '\115', '\040', '\161', '\165', '\040', '\061', '\012', '\162', + '\126', '\142', '\040', '\145', '\162', '\040', '\061', '\012', + '\161', '\132', '\162', '\040', '\161', '\165', '\040', '\061', + '\012', '\150', '\170', '\117', '\040', '\164', '\150', '\040', + '\061', '\012', '\167', '\124', '\156', '\040', '\141', '\156', + '\040', '\061', '\012', '\112', '\172', '\146', '\040', '\163', + '\172', '\040', '\061', '\012', '\121', '\152', '\142', '\040', + '\151', '\152', '\040', '\061', '\012', '\165', '\131', '\166', + '\040', '\161', '\165', '\040', '\061', '\012', '\160', '\167', + '\113', '\040', '\160', '\162', '\040', '\061', '\012', '\150', + '\166', '\110', '\040', '\164', '\150', '\040', '\061', '\012', + '\104', '\161', '\145', '\040', '\161', '\165', '\040', '\061', + '\012', '\160', '\146', '\111', '\040', '\160', '\162', '\040', + '\061', '\012', '\155', '\150', '\126', '\040', '\164', '\150', + '\040', '\061', '\012', '\152', '\147', '\105', '\040', '\156', + '\147', '\040', '\061', '\012', '\162', '\143', '\121', '\040', + '\143', '\150', '\040', '\061', '\012', '\153', '\155', '\124', + '\040', '\153', '\141', '\040', '\061', '\012', '\127', '\172', + '\152', '\040', '\163', '\172', '\040', '\061', '\012', '\170', + '\116', '\163', '\040', '\163', '\164', '\040', '\061', '\012', + '\120', '\142', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\172', '\166', '\102', '\040', '\163', '\172', '\040', + '\061', '\012', '\170', '\150', '\112', '\040', '\164', '\150', + '\040', '\061', '\012', '\163', '\166', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\116', '\166', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\163', '\167', '\132', + '\040', '\163', '\164', '\040', '\061', '\012', '\152', '\147', + '\106', '\040', '\156', '\147', '\040', '\061', '\012', '\155', + '\146', '\114', '\040', '\155', '\145', '\040', '\061', '\012', + '\172', '\153', '\114', '\040', '\163', '\172', '\040', '\061', + '\012', '\152', '\126', '\160', '\040', '\151', '\152', '\040', + '\061', '\012', '\104', '\153', '\152', '\040', '\151', '\152', + '\040', '\061', '\012', '\170', '\165', '\131', '\040', '\161', + '\165', '\040', '\061', '\012', '\150', '\110', '\161', '\040', + '\164', '\150', '\040', '\061', '\012', '\143', '\123', '\146', + '\040', '\143', '\150', '\040', '\061', '\012', '\112', '\172', + '\144', '\040', '\163', '\172', '\040', '\061', '\012', '\154', + '\161', '\125', '\040', '\161', '\165', '\040', '\061', '\012', + '\161', '\115', '\144', '\040', '\161', '\165', '\040', '\061', + '\012', '\121', '\147', '\152', '\040', '\156', '\147', '\040', + '\061', '\012', '\146', '\170', '\153', '\040', '\153', '\141', + '\040', '\061', '\012', '\164', '\122', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\172', '\106', '\153', '\040', + '\163', '\172', '\040', '\061', '\012', '\161', '\105', '\157', + '\040', '\161', '\165', '\040', '\061', '\012', '\166', '\157', + '\131', '\040', '\157', '\156', '\040', '\061', '\012', '\101', + '\167', '\152', '\040', '\151', '\152', '\040', '\061', '\012', + '\124', '\170', '\152', '\040', '\151', '\152', '\040', '\061', + '\012', '\143', '\111', '\147', '\040', '\143', '\150', '\040', + '\061', '\012', '\170', '\125', '\165', '\040', '\161', '\165', + '\040', '\061', '\012', '\163', '\122', '\162', '\040', '\145', + '\162', '\040', '\061', '\012', '\112', '\170', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\151', '\120', '\146', + '\040', '\151', '\156', '\040', '\061', '\012', '\145', '\152', + '\131', '\040', '\145', '\162', '\040', '\061', '\012', '\130', + '\164', '\163', '\040', '\164', '\150', '\040', '\061', '\012', + '\160', '\146', '\124', '\040', '\160', '\162', '\040', '\061', + '\012', '\120', '\161', '\141', '\040', '\141', '\156', '\040', + '\061', '\012', '\172', '\163', '\126', '\040', '\163', '\164', + '\040', '\061', '\012', '\171', '\160', '\103', '\040', '\160', + '\162', '\040', '\061', '\012', '\167', '\115', '\163', '\040', + '\163', '\164', '\040', '\061', '\012', '\161', '\105', '\143', + '\040', '\143', '\150', '\040', '\061', '\012', '\166', '\170', + '\131', '\040', '\166', '\141', '\040', '\061', '\012', '\146', + '\125', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\104', '\146', '\146', '\040', '\146', '\157', '\040', '\061', + '\012', '\147', '\161', '\121', '\040', '\161', '\165', '\040', + '\061', '\012', '\172', '\115', '\166', '\040', '\163', '\172', + '\040', '\061', '\012', '\166', '\112', '\151', '\040', '\151', + '\156', '\040', '\061', '\012', '\146', '\120', '\166', '\040', + '\166', '\141', '\040', '\061', '\012', '\144', '\114', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\143', '\144', + '\115', '\040', '\143', '\150', '\040', '\061', '\012', '\147', + '\116', '\170', '\040', '\156', '\147', '\040', '\061', '\012', + '\141', '\107', '\166', '\040', '\141', '\156', '\040', '\061', + '\012', '\166', '\166', '\104', '\040', '\166', '\141', '\040', + '\061', '\012', '\144', '\112', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\162', '\170', '\131', '\040', '\145', + '\162', '\040', '\061', '\012', '\162', '\127', '\152', '\040', + '\145', '\162', '\040', '\061', '\012', '\120', '\166', '\170', + '\040', '\166', '\141', '\040', '\061', '\012', '\162', '\150', + '\104', '\040', '\164', '\150', '\040', '\061', '\012', '\172', + '\122', '\144', '\040', '\163', '\172', '\040', '\061', '\012', + '\113', '\147', '\166', '\040', '\156', '\147', '\040', '\061', + '\012', '\130', '\166', '\171', '\040', '\166', '\141', '\040', + '\061', '\012', '\153', '\132', '\152', '\040', '\151', '\152', + '\040', '\061', '\012', '\153', '\160', '\113', '\040', '\153', + '\141', '\040', '\061', '\012', '\120', '\146', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\167', '\125', '\145', + '\040', '\145', '\162', '\040', '\061', '\012', '\167', '\127', + '\170', '\040', '\167', '\141', '\040', '\061', '\012', '\152', + '\120', '\167', '\040', '\151', '\152', '\040', '\061', '\012', + '\147', '\114', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\151', '\112', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\147', '\120', '\170', '\040', '\156', '\147', + '\040', '\061', '\012', '\152', '\110', '\144', '\040', '\144', + '\145', '\040', '\061', '\012', '\166', '\112', '\142', '\040', + '\166', '\141', '\040', '\061', '\012', '\170', '\150', '\102', + '\040', '\164', '\150', '\040', '\061', '\012', '\170', '\121', + '\166', '\040', '\166', '\141', '\040', '\061', '\012', '\105', + '\157', '\141', '\040', '\141', '\156', '\040', '\061', '\012', + '\160', '\152', '\117', '\040', '\151', '\152', '\040', '\061', + '\012', '\171', '\106', '\152', '\040', '\151', '\152', '\040', + '\061', '\012', '\163', '\130', '\157', '\040', '\157', '\156', + '\040', '\061', '\012', '\167', '\142', '\131', '\040', '\167', + '\141', '\040', '\061', '\012', '\143', '\152', '\117', '\040', + '\143', '\150', '\040', '\061', '\012', '\155', '\154', '\132', + '\040', '\154', '\145', '\040', '\061', '\012', '\142', '\116', + '\166', '\040', '\166', '\141', '\040', '\061', '\012', '\153', + '\152', '\120', '\040', '\151', '\152', '\040', '\061', '\012', + '\171', '\130', '\156', '\040', '\141', '\156', '\040', '\061', + '\012', '\161', '\126', '\152', '\040', '\161', '\165', '\040', + '\061', '\012', '\146', '\116', '\166', '\040', '\166', '\141', + '\040', '\061', '\012', '\147', '\152', '\127', '\040', '\156', + '\147', '\040', '\061', '\012', '\156', '\130', '\152', '\040', + '\141', '\156', '\040', '\061', '\012', '\144', '\161', '\112', + '\040', '\161', '\165', '\040', '\061', '\012', '\110', '\156', + '\150', '\040', '\164', '\150', '\040', '\061', '\012', '\121', + '\171', '\153', '\040', '\153', '\141', '\040', '\061', '\012', + '\153', '\166', '\102', '\040', '\153', '\141', '\040', '\061', + '\012', '\161', '\171', '\102', '\040', '\161', '\165', '\040', + '\061', '\012', '\155', '\104', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\172', '\147', '\120', '\040', '\156', + '\147', '\040', '\061', '\012', '\132', '\172', '\153', '\040', + '\163', '\172', '\040', '\061', '\012', '\146', '\115', '\153', + '\040', '\153', '\141', '\040', '\061', '\012', '\170', '\172', + '\131', '\040', '\163', '\172', '\040', '\061', '\012', '\161', + '\142', '\124', '\040', '\161', '\165', '\040', '\061', '\012', + '\170', '\117', '\164', '\040', '\164', '\150', '\040', '\061', + '\012', '\170', '\163', '\101', '\040', '\163', '\164', '\040', + '\061', '\012', '\147', '\114', '\152', '\040', '\156', '\147', + '\040', '\061', '\012', '\172', '\170', '\110', '\040', '\163', + '\172', '\040', '\061', '\012', '\143', '\114', '\155', '\040', + '\143', '\150', '\040', '\061', '\012', '\104', '\156', '\153', + '\040', '\141', '\156', '\040', '\061', '\012', '\172', '\111', + '\165', '\040', '\161', '\165', '\040', '\061', '\012', '\153', + '\160', '\112', '\040', '\153', '\141', '\040', '\061', '\012', + '\170', '\162', '\113', '\040', '\145', '\162', '\040', '\061', + '\012', '\145', '\111', '\142', '\040', '\145', '\162', '\040', + '\061', '\012', '\112', '\142', '\160', '\040', '\160', '\162', + '\040', '\061', '\012', '\102', '\161', '\147', '\040', '\161', + '\165', '\040', '\061', '\012', '\164', '\130', '\147', '\040', + '\164', '\150', '\040', '\061', '\012', '\132', '\152', '\153', + '\040', '\151', '\152', '\040', '\061', '\012', '\144', '\122', + '\144', '\040', '\144', '\145', '\040', '\061', '\012', '\164', + '\152', '\132', '\040', '\164', '\150', '\040', '\061', '\012', + '\150', '\121', '\154', '\040', '\164', '\150', '\040', '\061', + '\012', '\151', '\171', '\127', '\040', '\151', '\156', '\040', + '\061', '\012', '\112', '\167', '\144', '\040', '\144', '\145', + '\040', '\061', '\012', '\161', '\132', '\164', '\040', '\164', + '\150', '\040', '\061', '\012', '\143', '\112', '\160', '\040', + '\143', '\150', '\040', '\061', '\012', '\152', '\102', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\172', '\162', + '\107', '\040', '\145', '\162', '\040', '\061', '\012', '\150', + '\127', '\146', '\040', '\164', '\150', '\040', '\061', '\012', + '\132', '\144', '\163', '\040', '\163', '\164', '\040', '\061', + '\012', '\161', '\163', '\132', '\040', '\161', '\165', '\040', + '\061', '\012', '\143', '\121', '\170', '\040', '\143', '\150', + '\040', '\061', '\012', '\143', '\143', '\116', '\040', '\143', + '\150', '\040', '\061', '\012', '\171', '\167', '\115', '\040', + '\167', '\141', '\040', '\061', '\012', '\147', '\142', '\130', + '\040', '\156', '\147', '\040', '\061', '\012', '\164', '\146', + '\124', '\040', '\164', '\150', '\040', '\061', '\012', '\166', + '\167', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\121', '\142', '\160', '\040', '\160', '\162', '\040', '\061', + '\012', '\171', '\145', '\131', '\040', '\145', '\162', '\040', + '\061', '\012', '\141', '\125', '\142', '\040', '\141', '\156', + '\040', '\061', '\012', '\161', '\110', '\167', '\040', '\161', + '\165', '\040', '\061', '\012', '\106', '\150', '\161', '\040', + '\164', '\150', '\040', '\061', '\012', '\106', '\156', '\147', + '\040', '\141', '\156', '\040', '\061', '\012', '\154', '\166', + '\111', '\040', '\154', '\145', '\040', '\061', '\012', '\152', + '\103', '\146', '\040', '\151', '\152', '\040', '\061', '\012', + '\150', '\161', '\110', '\040', '\164', '\150', '\040', '\061', + '\012', '\164', '\124', '\161', '\040', '\164', '\150', '\040', + '\061', '\012', '\163', '\146', '\111', '\040', '\163', '\164', + '\040', '\061', '\012', '\166', '\163', '\115', '\040', '\163', + '\164', '\040', '\061', '\012', '\154', '\104', '\160', '\040', + '\154', '\145', '\040', '\061', '\012', '\167', '\112', '\142', + '\040', '\167', '\141', '\040', '\061', '\012', '\142', '\150', + '\130', '\040', '\164', '\150', '\040', '\061', '\012', '\162', + '\122', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\161', '\164', '\123', '\040', '\164', '\150', '\040', '\061', + '\012', '\132', '\167', '\160', '\040', '\160', '\162', '\040', + '\061', '\012', '\112', '\142', '\150', '\040', '\164', '\150', + '\040', '\061', '\012', '\150', '\110', '\142', '\040', '\164', + '\150', '\040', '\061', '\012', '\160', '\104', '\171', '\040', + '\160', '\162', '\040', '\061', '\012', '\163', '\152', '\104', + '\040', '\163', '\164', '\040', '\061', '\012', '\117', '\171', + '\160', '\040', '\160', '\162', '\040', '\061', '\012', '\161', + '\167', '\104', '\040', '\161', '\165', '\040', '\061', '\012', + '\152', '\142', '\104', '\040', '\151', '\152', '\040', '\061', + '\012', '\166', '\160', '\107', '\040', '\166', '\141', '\040', + '\061', '\012', '\127', '\152', '\142', '\040', '\151', '\152', + '\040', '\061', '\012', '\166', '\160', '\102', '\040', '\166', + '\141', '\040', '\061', '\012', '\141', '\130', '\161', '\040', + '\141', '\156', '\040', '\061', '\012', '\155', '\127', '\172', + '\040', '\163', '\172', '\040', '\061', '\012', '\161', '\110', + '\151', '\040', '\161', '\165', '\040', '\061', '\012', '\146', + '\171', '\116', '\040', '\156', '\171', '\040', '\061', '\012', + '\155', '\142', '\121', '\040', '\155', '\145', '\040', '\061', + '\012', '\171', '\167', '\103', '\040', '\167', '\141', '\040', + '\061', '\012', '\157', '\126', '\147', '\040', '\156', '\147', + '\040', '\061', '\012', '\170', '\155', '\132', '\040', '\155', + '\145', '\040', '\061', '\012', '\163', '\154', '\117', '\040', + '\154', '\145', '\040', '\061', '\012', '\146', '\130', '\156', + '\040', '\141', '\156', '\040', '\061', '\012', '\153', '\131', + '\163', '\040', '\163', '\164', '\040', '\061', '\012', '\160', + '\126', '\165', '\040', '\161', '\165', '\040', '\061', '\012', + '\142', '\153', '\125', '\040', '\153', '\141', '\040', '\061', + '\012', '\102', '\162', '\161', '\040', '\161', '\165', '\040', + '\061', '\012', '\161', '\103', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\130', '\143', '\170', '\040', '\143', + '\150', '\040', '\061', '\012', '\172', '\115', '\164', '\040', + '\164', '\150', '\040', '\061', '\012', '\143', '\122', '\167', + '\040', '\143', '\150', '\040', '\061', '\012', '\147', '\172', + '\121', '\040', '\156', '\147', '\040', '\061', '\012', '\121', + '\142', '\147', '\040', '\156', '\147', '\040', '\061', '\012', + '\152', '\165', '\125', '\040', '\161', '\165', '\040', '\061', + '\012', '\170', '\123', '\172', '\040', '\163', '\172', '\040', + '\061', '\012', '\126', '\147', '\172', '\040', '\156', '\147', + '\040', '\061', '\012', '\157', '\115', '\167', '\040', '\157', + '\156', '\040', '\061', '\012', '\146', '\160', '\105', '\040', + '\160', '\162', '\040', '\061', '\012', '\170', '\152', '\130', + '\040', '\151', '\152', '\040', '\061', '\012', '\161', '\103', + '\147', '\040', '\161', '\165', '\040', '\061', '\012', '\172', + '\167', '\115', '\040', '\163', '\172', '\040', '\061', '\012', + '\165', '\121', '\154', '\040', '\161', '\165', '\040', '\061', + '\012', '\161', '\120', '\153', '\040', '\161', '\165', '\040', + '\061', '\012', '\160', '\152', '\104', '\040', '\151', '\152', + '\040', '\061', '\012', '\121', '\172', '\155', '\040', '\163', + '\172', '\040', '\061', '\012', '\163', '\111', '\160', '\040', + '\163', '\164', '\040', '\061', '\012', '\165', '\157', '\107', + '\040', '\161', '\165', '\040', '\061', '\012', '\162', '\126', + '\154', '\040', '\145', '\162', '\040', '\061', '\012', '\143', + '\142', '\113', '\040', '\143', '\150', '\040', '\061', '\012', + '\150', '\130', '\155', '\040', '\164', '\150', '\040', '\061', + '\012', '\113', '\163', '\146', '\040', '\163', '\164', '\040', + '\061', '\012', '\153', '\142', '\106', '\040', '\153', '\141', + '\040', '\061', '\012', '\167', '\102', '\155', '\040', '\155', + '\145', '\040', '\061', '\012', '\151', '\131', '\164', '\040', + '\164', '\150', '\040', '\061', '\012', '\163', '\147', '\110', + '\040', '\156', '\147', '\040', '\061', '\012', '\107', '\172', + '\166', '\040', '\163', '\172', '\040', '\061', '\012', '\171', + '\166', '\105', '\040', '\166', '\141', '\040', '\061', '\012', + '\170', '\113', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\163', '\127', '\146', '\040', '\163', '\164', '\040', + '\061', '\012', '\172', '\102', '\143', '\040', '\143', '\150', + '\040', '\061', '\012', '\171', '\153', '\110', '\040', '\153', + '\141', '\040', '\061', '\012', '\166', '\152', '\110', '\040', + '\151', '\152', '\040', '\061', '\012', '\167', '\150', '\111', + '\040', '\164', '\150', '\040', '\061', '\012', '\166', '\120', + '\152', '\040', '\151', '\152', '\040', '\061', '\012', '\132', + '\150', '\164', '\040', '\164', '\150', '\040', '\061', '\012', + '\151', '\112', '\170', '\040', '\151', '\156', '\040', '\061', + '\012', '\143', '\132', '\164', '\040', '\164', '\150', '\040', + '\061', '\012', '\144', '\161', '\125', '\040', '\161', '\165', + '\040', '\061', '\012', '\150', '\115', '\144', '\040', '\164', + '\150', '\040', '\061', '\012', '\143', '\125', '\152', '\040', + '\143', '\150', '\040', '\061', '\012', '\166', '\115', '\147', + '\040', '\156', '\147', '\040', '\061', '\012', '\160', '\143', + '\112', '\040', '\143', '\150', '\040', '\061', '\012', '\102', + '\143', '\155', '\040', '\143', '\150', '\040', '\061', '\012', + '\152', '\130', '\151', '\040', '\151', '\156', '\040', '\061', + '\012', '\170', '\157', '\111', '\040', '\157', '\156', '\040', + '\061', '\012', '\132', '\153', '\161', '\040', '\161', '\165', + '\040', '\061', '\012', '\130', '\172', '\162', '\040', '\145', + '\162', '\040', '\061', '\012', '\171', '\172', '\115', '\040', + '\163', '\172', '\040', '\061', '\012', '\161', '\152', '\130', + '\040', '\161', '\165', '\040', '\061', '\012', '\155', '\116', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\150', + '\160', '\130', '\040', '\164', '\150', '\040', '\061', '\012', + '\146', '\102', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\164', '\130', '\144', '\040', '\164', '\150', '\040', + '\061', '\012', '\130', '\153', '\151', '\040', '\151', '\156', + '\040', '\061', '\012', '\110', '\163', '\161', '\040', '\161', + '\165', '\040', '\061', '\012', '\142', '\161', '\125', '\040', + '\161', '\165', '\040', '\061', '\012', '\163', '\147', '\106', + '\040', '\156', '\147', '\040', '\061', '\012', '\144', '\120', + '\143', '\040', '\143', '\150', '\040', '\061', '\012', '\112', + '\170', '\151', '\040', '\151', '\156', '\040', '\061', '\012', + '\125', '\147', '\160', '\040', '\156', '\147', '\040', '\061', + '\012', '\122', '\170', '\151', '\040', '\151', '\156', '\040', + '\061', '\012', '\113', '\167', '\155', '\040', '\155', '\145', + '\040', '\061', '\012', '\172', '\153', '\104', '\040', '\163', + '\172', '\040', '\061', '\012', '\122', '\161', '\154', '\040', + '\161', '\165', '\040', '\061', '\012', '\160', '\112', '\142', + '\040', '\160', '\162', '\040', '\061', '\012', '\146', '\143', + '\126', '\040', '\143', '\150', '\040', '\061', '\012', '\151', + '\126', '\144', '\040', '\151', '\156', '\040', '\061', '\012', + '\142', '\102', '\160', '\040', '\142', '\145', '\040', '\061', + '\012', '\117', '\152', '\167', '\040', '\151', '\152', '\040', + '\061', '\012', '\166', '\132', '\154', '\040', '\154', '\145', + '\040', '\061', '\012', '\111', '\171', '\152', '\040', '\151', + '\152', '\040', '\061', '\012', '\146', '\153', '\125', '\040', + '\153', '\141', '\040', '\061', '\012', '\113', '\143', '\161', + '\040', '\143', '\150', '\040', '\061', '\012', '\144', '\102', + '\161', '\040', '\161', '\165', '\040', '\061', '\012', '\115', + '\161', '\161', '\040', '\161', '\165', '\040', '\061', '\012', + '\151', '\115', '\147', '\040', '\156', '\147', '\040', '\061', + '\012', '\127', '\167', '\163', '\040', '\163', '\164', '\040', + '\061', '\012', '\164', '\161', '\130', '\040', '\164', '\150', + '\040', '\061', '\012', '\170', '\150', '\104', '\040', '\164', + '\150', '\040', '\061', '\012', '\162', '\116', '\154', '\040', + '\145', '\162', '\040', '\061', '\012', '\160', '\127', '\144', + '\040', '\144', '\145', '\040', '\061', '\012', '\152', '\162', + '\126', '\040', '\145', '\162', '\040', '\061', '\012', '\102', + '\155', '\152', '\040', '\151', '\152', '\040', '\061', '\012', + '\110', '\155', '\161', '\040', '\161', '\165', '\040', '\061', + '\012', '\166', '\154', '\110', '\040', '\154', '\145', '\040', + '\061', '\012', '\115', '\170', '\142', '\040', '\142', '\145', + '\040', '\061', '\012', '\171', '\171', '\123', '\040', '\156', + '\171', '\040', '\061', '\012', '\161', '\166', '\127', '\040', + '\161', '\165', '\040', '\061', '\012', '\146', '\166', '\130', + '\040', '\166', '\141', '\040', '\061', '\012', '\126', '\146', + '\145', '\040', '\145', '\162', '\040', '\061', '\012', '\103', + '\144', '\167', '\040', '\144', '\145', '\040', '\061', '\012', + '\113', '\147', '\145', '\040', '\156', '\147', '\040', '\061', + '\012', '\121', '\145', '\152', '\040', '\145', '\162', '\040', + '\061', '\012', '\162', '\166', '\132', '\040', '\145', '\162', + '\040', '\061', '\012', '\166', '\172', '\111', '\040', '\163', + '\172', '\040', '\061', '\012', '\144', '\104', '\156', '\040', + '\141', '\156', '\040', '\061', '\012', '\156', '\167', '\123', + '\040', '\141', '\156', '\040', '\061', '\012', '\121', '\143', + '\142', '\040', '\143', '\150', '\040', '\061', '\012', '\167', + '\153', '\126', '\040', '\153', '\141', '\040', '\061', '\012', + '\165', '\103', '\170', '\040', '\161', '\165', '\040', '\061', + '\012', '\111', '\147', '\153', '\040', '\156', '\147', '\040', + '\061', '\012', '\126', '\160', '\155', '\040', '\155', '\145', + '\040', '\061', '\012', '\150', '\102', '\155', '\040', '\164', + '\150', '\040', '\061', '\012', '\160', '\144', '\121', '\040', + '\144', '\145', '\040', '\061', '\012', '\146', '\147', '\121', + '\040', '\156', '\147', '\040', '\061', '\012', '\171', '\121', + '\155', '\040', '\155', '\145', '\040', '\061', '\012', '\147', + '\170', '\110', '\040', '\156', '\147', '\040', '\061', '\012', + '\160', '\161', '\113', '\040', '\161', '\165', '\040', '\061', + '\012', '\154', '\122', '\143', '\040', '\143', '\150', '\040', + '\061', '\012', '\130', '\144', '\166', '\040', '\144', '\145', + '\040', '\061', '\012', '\150', '\104', '\172', '\040', '\164', + '\150', '\040', '\061', '\012', '\144', '\106', '\167', '\040', + '\144', '\145', '\040', '\061', '\012', '\161', '\121', '\165', + '\040', '\165', '\156', '\040', '\061', '\012', '\170', '\142', + '\104', '\040', '\142', '\145', '\040', '\061', '\012', '\161', + '\155', '\105', '\040', '\161', '\165', '\040', '\061', '\012', + '\155', '\127', '\155', '\040', '\155', '\145', '\040', '\061', + '\012', '\152', '\102', '\142', '\040', '\151', '\152', '\040', + '\061', '\012', '\152', '\130', '\164', '\040', '\164', '\150', + '\040', '\061', '\012', '\146', '\170', '\125', '\040', '\146', +}; + +extern const int ksizeofUniversalAmbigsFile = sizeof(kUniversalAmbigsFile); + +} // namespace tesseract diff --git a/ccutil/universalambigs.h b/ccutil/universalambigs.h new file mode 100644 index 0000000000..bcc633e8cc --- /dev/null +++ b/ccutil/universalambigs.h @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////// +// File: universalambigs.h +// Description: Data for a universal ambigs file that is useful for +// any language. +// Author: Ray Smith +// Created: Mon Mar 18 11:26:00 PDT 2013 +// +// (C) Copyright 2013, Google Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +/////////////////////////////////////////////////////////////////////// + +namespace tesseract { + +extern const char kUniversalAmbigsFile[]; +extern const int ksizeofUniversalAmbigsFile; + +} // namespace tesseract diff --git a/configure.ac b/configure.ac index 440375d7a5..2ef0f9576e 100644 --- a/configure.ac +++ b/configure.ac @@ -7,7 +7,7 @@ # ---------------------------------------- AC_PREREQ(2.50) -AC_INIT([tesseract], [3.02.03], [http://code.google.com/p/tesseract-ocr/issues/list]) +AC_INIT([tesseract], [3.03], [http://code.google.com/p/tesseract-ocr/issues/list]) AC_CONFIG_MACRO_DIR([m4]) AC_REVISION($Id: configure.ac,v 1.4 2007/02/02 22:38:17 theraysmith Exp $) AC_CONFIG_AUX_DIR(config) @@ -18,7 +18,7 @@ AC_PREFIX_DEFAULT(/usr/local) # documentation. # TODO(luc) Generate good documentation using doxygen or equivalent PACKAGE_YEAR=2013 -PACKAGE_DATE="07/03" +PACKAGE_DATE="08/13" AC_DEFINE_UNQUOTED(PACKAGE_NAME,["${PACKAGE_NAME}"],[Name of package]) AC_DEFINE_UNQUOTED(PACKAGE_VERSION,["${PACKAGE_VERSION}"],[Version number]) @@ -34,8 +34,8 @@ GENERIC_LIBRARY_NAME=tesseract # Release versioning GENERIC_MAJOR_VERSION=3 -GENERIC_MINOR_VERSION=2 -GENERIC_MICRO_VERSION=3 +GENERIC_MINOR_VERSION=3 +GENERIC_MICRO_VERSION=0 # API version (often = GENERIC_MAJOR_VERSION.GENERIC_MINOR_VERSION) GENERIC_API_VERSION=$GENERIC_MAJOR_VERSION.$GENERIC_MINOR_VERSION @@ -248,6 +248,7 @@ AC_HEADER_TIME AC_HEADER_SYS_WAIT AC_CHECK_HEADERS(sys/ipc.h sys/shm.h) AC_CHECK_HEADERS(limits.h malloc.h) +AC_CHECK_HEADERS(allheaders.h) # Enable use of system-defined bool type if available: AC_HEADER_STDBOOL @@ -261,6 +262,7 @@ AC_SYS_LARGEFILE # ---------------------------------------- AC_CHECK_TYPES(wchar_t) +AC_CHECK_TYPES(long long int) AC_CHECK_TYPES(mbstate_t,,,[#include "wchar.h"]) # ---------------------------------------- diff --git a/cube/char_set.cpp b/cube/char_set.cpp index 16dba49456..1414d640f4 100644 --- a/cube/char_set.cpp +++ b/cube/char_set.cpp @@ -65,13 +65,13 @@ CharSet *CharSet::Create(TessdataManager *tessdata_manager, !tessdata_manager->SeekToStart(TESSDATA_UNICHARSET)) { fprintf(stderr, "Cube ERROR (CharSet::Create): could not find " "either cube or tesseract unicharset\n"); - return false; + return NULL; } FILE *charset_fp = tessdata_manager->GetDataFilePtr(); if (!charset_fp) { fprintf(stderr, "Cube ERROR (CharSet::Create): could not load " "a unicharset\n"); - return false; + return NULL; } // If we found a cube unicharset separate from tesseract's, load it and @@ -90,7 +90,7 @@ CharSet *CharSet::Create(TessdataManager *tessdata_manager, } if (!loaded) { delete char_set; - return false; + return NULL; } char_set->init_ = true; diff --git a/cube/conv_net_classifier.cpp b/cube/conv_net_classifier.cpp index aa6cf9d3c1..a5522e982c 100644 --- a/cube/conv_net_classifier.cpp +++ b/cube/conv_net_classifier.cpp @@ -234,8 +234,8 @@ bool ConvNetCharClassifier::LoadFoldingSets(const string &data_file_path, fclose(fp); string fold_sets_str; - if (!CubeUtils::ReadFileToString(fold_file_name.c_str(), - &fold_sets_str)) { + if (!CubeUtils::ReadFileToString(fold_file_name, + &fold_sets_str)) { return false; } @@ -327,7 +327,7 @@ bool ConvNetCharClassifier::LoadNets(const string &data_file_path, fclose(fp); // load main net - char_net_ = tesseract::NeuralNet::FromFile(char_net_file.c_str()); + char_net_ = tesseract::NeuralNet::FromFile(char_net_file); if (char_net_ == NULL) { fprintf(stderr, "Cube ERROR (ConvNetCharClassifier::LoadNets): " "could not load %s\n", char_net_file.c_str()); diff --git a/cube/cube_line_segmenter.cpp b/cube/cube_line_segmenter.cpp index deee573b8b..3f0b762a3f 100644 --- a/cube/cube_line_segmenter.cpp +++ b/cube/cube_line_segmenter.cpp @@ -124,7 +124,7 @@ Pixa *CubeLineSegmenter::CrackLine(Pix *cracked_line_pix, if (line_con_comps == NULL) { delete []lines_pixa; - return false; + return NULL; } // assign each conn comp to the a line based on its centroid @@ -142,7 +142,7 @@ Pixa *CubeLineSegmenter::CrackLine(Pix *cracked_line_pix, delete []lines_pixa; boxaDestroy(&line_con_comps); pixaDestroy(&line_con_comps_pix); - return false; + return NULL; } } @@ -413,14 +413,14 @@ Pix *CubeLineSegmenter::Pixa2Pix(Pixa *pixa, Box **dest_box, (*dest_box) = boxCreate(min_x, min_y, max_x - min_x, max_y - min_y); if ((*dest_box) == NULL) { - return false; + return NULL; } // create the union pix Pix *union_pix = pixCreate((*dest_box)->w, (*dest_box)->h, img_->d); if (union_pix == NULL) { boxDestroy(dest_box); - return false; + return NULL; } // create a pix corresponding to the union of all pixs diff --git a/cube/cube_object.cpp b/cube/cube_object.cpp index 9d31240001..aa02132735 100644 --- a/cube/cube_object.cpp +++ b/cube/cube_object.cpp @@ -165,7 +165,7 @@ WordAltList *CubeObject::Recognize(LangModel *lang_mod, bool word_mode) { if (deslanted_beam_obj_ == NULL) { fprintf(stderr, "Cube ERROR (CubeObject::Recognize): could not " "construct deslanted BeamSearch\n"); - return false; + return NULL; } } diff --git a/cube/hybrid_neural_net_classifier.cpp b/cube/hybrid_neural_net_classifier.cpp index 2315472799..b5822f6f22 100644 --- a/cube/hybrid_neural_net_classifier.cpp +++ b/cube/hybrid_neural_net_classifier.cpp @@ -230,8 +230,8 @@ bool HybridNeuralNetCharClassifier::LoadFoldingSets( fclose(fp); string fold_sets_str; - if (!CubeUtils::ReadFileToString(fold_file_name.c_str(), - &fold_sets_str)) { + if (!CubeUtils::ReadFileToString(fold_file_name, + &fold_sets_str)) { return false; } @@ -323,7 +323,7 @@ bool HybridNeuralNetCharClassifier::LoadNets(const string &data_file_path, fclose(fp); string str; - if (!CubeUtils::ReadFileToString(hybrid_net_file.c_str(), &str)) { + if (!CubeUtils::ReadFileToString(hybrid_net_file, &str)) { return false; } @@ -348,7 +348,7 @@ bool HybridNeuralNetCharClassifier::LoadNets(const string &data_file_path, } // load the net string net_file_name = data_file_path + tokens_vec[0]; - nets_[net_idx] = tesseract::NeuralNet::FromFile(net_file_name.c_str()); + nets_[net_idx] = tesseract::NeuralNet::FromFile(net_file_name); if (nets_[net_idx] == NULL) { return false; } diff --git a/cube/tess_lang_mod_edge.cpp b/cube/tess_lang_mod_edge.cpp index 16d64a3800..a71dfbce74 100644 --- a/cube/tess_lang_mod_edge.cpp +++ b/cube/tess_lang_mod_edge.cpp @@ -107,7 +107,7 @@ int TessLangModEdge::CreateChildren(CubeRecoContext *cntxt, LangModEdge **edge_array) { int edge_cnt = 0; NodeChildVector vec; - dawg->unichar_ids_of(parent_node, &vec); // find all children of the parent + dawg->unichar_ids_of(parent_node, &vec, false); // find all children for (int i = 0; i < vec.size(); ++i) { const NodeChild &child = vec[i]; if (child.unichar_id == INVALID_UNICHAR_ID) continue; diff --git a/cube/word_list_lang_model.cpp b/cube/word_list_lang_model.cpp index c939941c01..8537d256a8 100644 --- a/cube/word_list_lang_model.cpp +++ b/cube/word_list_lang_model.cpp @@ -74,7 +74,7 @@ LangModEdge **WordListLangModel::GetEdges(CharAltList *alt_list, // initialize if necessary if (init_ == false) { if (Init() == false) { - return false; + return NULL; } } @@ -92,7 +92,7 @@ LangModEdge **WordListLangModel::GetEdges(CharAltList *alt_list, // advance node edge_ref = dawg_->next_node(edge_ref); if (edge_ref == 0) { - return 0; + return NULL; } } diff --git a/cutil/Makefile.am b/cutil/Makefile.am index 1028d94883..15b339c8f3 100644 --- a/cutil/Makefile.am +++ b/cutil/Makefile.am @@ -8,7 +8,7 @@ endif noinst_HEADERS = \ bitvec.h callcpp.h const.h cutil.h cutil_class.h danerror.h efio.h \ emalloc.h freelist.h globals.h listio.h \ - oldheap.h oldlist.h structures.h tessarray.h + oldlist.h structures.h if !USING_MULTIPLELIBS noinst_LTLIBRARIES = libtesseract_cutil.la @@ -22,7 +22,7 @@ endif libtesseract_cutil_la_SOURCES = \ bitvec.cpp callcpp.cpp cutil.cpp cutil_class.cpp danerror.cpp efio.cpp \ - emalloc.cpp freelist.cpp listio.cpp oldheap.cpp \ - oldlist.cpp structures.cpp tessarray.cpp + emalloc.cpp freelist.cpp listio.cpp \ + oldlist.cpp structures.cpp diff --git a/cutil/bitvec.cpp b/cutil/bitvec.cpp index 75b10a787b..fadb4b44d1 100644 --- a/cutil/bitvec.cpp +++ b/cutil/bitvec.cpp @@ -73,27 +73,6 @@ void FreeBitVector(BIT_VECTOR BitVector) { } /* FreeBitVector */ -/** - * hamming_distance(array1,array2,length) computes the hamming distance - * between two bit strings. - */ -/*--------------------------------------------------------------------------*/ -int hamming_distance(uinT32* array1, uinT32* array2, int length) { - register uinT32 diff; /*bit difference */ - register int dist; /*total distance */ - - dist = 0; - for (; length > 0; length--) { - diff = *array1++ ^ *array2++;/*different bits */ - while (diff) { - diff &= diff - 1; /*lose a bit */ - dist++; - } - } - return dist; /*total distance */ -} - - /*---------------------------------------------------------------------------*/ /** * Allocate and return a new bit vector large enough to diff --git a/cutil/bitvec.h b/cutil/bitvec.h index 2b058930d9..af84ad60a3 100644 --- a/cutil/bitvec.h +++ b/cutil/bitvec.h @@ -70,8 +70,6 @@ BIT_VECTOR ExpandBitVector(BIT_VECTOR Vector, int NewNumBits); void FreeBitVector(BIT_VECTOR BitVector); -int hamming_distance(uinT32* array1, uinT32* array2, int length); - BIT_VECTOR NewBitVector(int NumBits); #endif diff --git a/cutil/danerror.cpp b/cutil/danerror.cpp index 58f8882fee..0f0ead46b2 100644 --- a/cutil/danerror.cpp +++ b/cutil/danerror.cpp @@ -53,5 +53,5 @@ void DoError(int Error, const char *Message) { tprintf("\nError: %s!\n", Message); } - signal_termination_handler(Error); + err_exit(); } /* DoError */ diff --git a/cutil/listio.cpp b/cutil/listio.cpp index eb8c1ee823..475088f5b5 100644 --- a/cutil/listio.cpp +++ b/cutil/listio.cpp @@ -46,7 +46,6 @@ LIST read_list(const char *filename) { FILE *infile; char s[CHARS_PER_LINE]; LIST list; - char *chopAt250(); if ((infile = open_file (filename, "r")) == NULL) return (NIL_LIST); diff --git a/cutil/oldheap.cpp b/cutil/oldheap.cpp deleted file mode 100644 index d0064c7461..0000000000 --- a/cutil/oldheap.cpp +++ /dev/null @@ -1,334 +0,0 @@ -/****************************************************************************** - ** Filename: heap.c - ** Purpose: Routines for managing heaps (smallest at root) - ** Author: Dan Johnson - ** History: 3/13/89, DSJ, Created. - ** - ** (c) Copyright Hewlett-Packard Company, 1988. - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** http://www.apache.org/licenses/LICENSE-2.0 - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - ******************************************************************************/ -/*----------------------------------------------------------------------------- - Include Files and Type Defines ------------------------------------------------------------------------------*/ -#include "oldheap.h" -#include "freelist.h" -#include "danerror.h" -#include "emalloc.h" -#include - -#define FATHER(N) ((N)>>1) -#define LEFTSON(N) ((N)<<1) -#define RIGHTSON(N) ((N)<<1 + 1) - -/*----------------------------------------------------------------------------- - Public Code ------------------------------------------------------------------------------*/ -/*---------------------------------------------------------------------------*/ -/** - * This routine creates and initializes a new heap data - * structure containing Size elements. In actuality, Size + 1 - * elements are allocated. The first element, element 0, is - * unused, this makes the index arithmetic easier. - * - * Globals: - * - None - * - * @param Size maximum number of entries in the heap - * @return Pointer to the new heap. - * @note Exceptions: None - * @note History: 3/13/89, DSJ, Created. - */ -HEAP *MakeHeap(int Size) { - HEAP *NewHeap; - - NewHeap = (HEAP *) Emalloc (sizeof (HEAP) + Size * sizeof (HEAPENTRY)); - - NewHeap->Size = Size; - NewHeap->FirstFree = 1; - return (NewHeap); -} /* MakeHeap */ - - -/*---------------------------------------------------------------------------*/ -/** - * This routine removes the top item on the heap and places - * its contents into Key and Data. - * - * Globals: - * - None - * - * @param Heap ptr to heap whose top is to be removed and returned - * @param Key place to put key of top heap item - * @param out_ptr place to put data of top heap item - * - * @return OK if top entry returned, EMPTY if heap is empty - * @note Exceptions: None - * @note History: 5/10/91, DSJ, Created (Modified from GetTopOfHeap). - */ -int HeapPop(HEAP *Heap, FLOAT32 *Key, void *out_ptr) { - inT32 Hole; - FLOAT32 HoleKey; - inT32 Son; - void **Data = (void **) out_ptr; - - if (Heap->FirstFree <= 1) - return (EMPTY); - - *Key = Heap->Entry[1].Key; - *Data = Heap->Entry[1].Data; - - Heap->FirstFree--; - - /* imagine the hole at the root is filled with the last entry in the heap */ - HoleKey = Heap->Entry[Heap->FirstFree].Key; - Hole = 1; - - /* while hole has 2 sons */ - while ((Son = LEFTSON (Hole)) < Heap->FirstFree) { - /* find the son with the smallest key */ - if (Heap->Entry[Son].Key > Heap->Entry[Son + 1].Key) - Son++; - - /* if key for hole is greater than key for son, sift hole down */ - if (HoleKey > Heap->Entry[Son].Key) { - Heap->Entry[Hole].Key = Heap->Entry[Son].Key; - Heap->Entry[Hole].Data = Heap->Entry[Son].Data; - Hole = Son; - } - else - break; - } - Heap->Entry[Hole].Key = HoleKey; - Heap->Entry[Hole].Data = Heap->Entry[Heap->FirstFree].Data; - return (TESS_HEAP_OK); -} /* HeapPop */ - - -/** - * HeapPopWorst - * - * Remove the largest item from the heap. - * - * @param Heap ptr to heap whose top is to be removed and returned - * @param Key place to put key of top heap item - * @param out_ptr place to put data of top heap item - */ -int HeapPopWorst(HEAP *Heap, FLOAT32 *Key, void *out_ptr) { - inT32 Index; /*current index */ - inT32 Hole; - FLOAT32 HoleKey; - inT32 Father; - void *HoleData; - void **Data = (void **) out_ptr; - - if (Heap->FirstFree <= 1) - return (EMPTY); - - HoleKey = Heap->Entry[1].Key; - Hole = 1; - Heap->FirstFree--; - for (Index = Heap->FirstFree, Father = FATHER (Index); Index > Father; - Index--) - if (Heap->Entry[Index].Key > HoleKey) { - /*find biggest */ - HoleKey = Heap->Entry[Index].Key; - Hole = Index; - } - *Key = HoleKey; - *Data = Heap->Entry[Hole].Data; - - HoleKey = Heap->Entry[Heap->FirstFree].Key; - Heap->Entry[Hole].Key = HoleKey; - HoleData = Heap->Entry[Heap->FirstFree].Data; - Heap->Entry[Hole].Data = HoleData; - - /* now sift last entry to its rightful place */ - Father = FATHER (Hole); /*father of hole */ - while (Hole > 1 && Heap->Entry[Father].Key > HoleKey) { - /*swap entries */ - Heap->Entry[Hole].Key = Heap->Entry[Father].Key; - Heap->Entry[Hole].Data = Heap->Entry[Father].Data; - Heap->Entry[Father].Data = HoleData; - Heap->Entry[Father].Key = HoleKey; - Hole = Father; - Father = FATHER (Hole); - } - return (TESS_HEAP_OK); -} /* HeapPop */ - - -// Pushes data onto the heap only if there is free space left. -// Returns true if data was added to the heap, false if the heap was full. -bool HeapPushCheckSize(HEAP *Heap, FLOAT32 Key, void *Data) { - if (Heap->FirstFree > Heap->Size) return false; - HeapPush(Heap, Key, Data); - return true; -} - -/*---------------------------------------------------------------------------*/ -/** - * This routine stores Data into Heap and associates it - * with Key. The heap is - * maintained in such a way that the item with the lowest key - * is always at the top of the heap. - * - * Globals: - * - None - * - * @param Heap ptr to heap to store new item in - * @param Key numeric key associated with new item - * @param Data ptr to data contents of new item - * - * @note Exceptions: - * - HEAPFULL error if heap size is exceeded - * - * @note History: 5/10/91, DSJ, Created (Modified version of HeapStore). - */ -void HeapPush(HEAP *Heap, FLOAT32 Key, void *Data) { - inT32 Item; - inT32 Father; - - if (Heap->FirstFree > Heap->Size) - DoError (HEAPFULL, "Heap size exceeded"); - - Item = Heap->FirstFree; - Heap->FirstFree++; - while (Item != 1) { - Father = FATHER (Item); - if (Heap->Entry[Father].Key > Key) { - Heap->Entry[Item].Key = Heap->Entry[Father].Key; - Heap->Entry[Item].Data = Heap->Entry[Father].Data; - Item = Father; - } - else - break; - } - Heap->Entry[Item].Key = Key; - Heap->Entry[Item].Data = Data; -} /* HeapPush */ - - -/*---------------------------------------------------------------------------*/ -/** - * This routine stores Entry into Heap. The heap is - * maintained in such a way that the item with the lowest key - * is always at the top of the heap. - * - * Globals: - * - None - * - * @param Heap ptr to heap to store new item in - * @param Entry ptr to item to be stored in Heap - * @note Exceptions: - * - HEAPFULL error if heap size is exceeded - * @note History: 3/13/89, DSJ, Created. - */ -void HeapStore(HEAP *Heap, HEAPENTRY *Entry) { - inT32 Item; - inT32 Father; - - if (Heap->FirstFree > Heap->Size) - DoError (HEAPFULL, "Heap size exceeded"); - - Item = Heap->FirstFree; - Heap->FirstFree++; - while (Item != 1) { - Father = FATHER (Item); - if (Heap->Entry[Father].Key > Entry->Key) { - Heap->Entry[Item].Key = Heap->Entry[Father].Key; - Heap->Entry[Item].Data = Heap->Entry[Father].Data; - Item = Father; - } - else - break; - } - Heap->Entry[Item].Key = Entry->Key; - Heap->Entry[Item].Data = Entry->Data; -} /* HeapStore */ - - -/*---------------------------------------------------------------------------*/ -/** - * This routine removes the top item on the heap and copies its - * contents into Entry. - * - * @param Heap ptr to heap whose top is to be removed and returned - * @param Entry ptr to heap entry to be filled with top entry on Heap - * - * Globals: - * - None - * - * @return OK if top entry returned, EMPTY if heap is empty - * @note Exceptions: None - * @note History: 3/13/89, DSJ, Created. - */ -int GetTopOfHeap(HEAP *Heap, HEAPENTRY *Entry) { - inT32 Hole; - FLOAT32 HoleKey; - inT32 Son; - - if (Heap->FirstFree <= 1) - return (EMPTY); - - Entry->Key = Heap->Entry[1].Key; - Entry->Data = Heap->Entry[1].Data; - - Heap->FirstFree--; - - /* imagine the hole at the root is filled with the last entry in the heap */ - HoleKey = Heap->Entry[Heap->FirstFree].Key; - Hole = 1; - - /* while hole has 2 sons */ - while ((Son = LEFTSON (Hole)) < Heap->FirstFree) { - /* find the son with the smallest key */ - if (Heap->Entry[Son].Key > Heap->Entry[Son + 1].Key) - Son++; - - /* if key for hole is greater than key for son, sift hole down */ - if (HoleKey > Heap->Entry[Son].Key) { - Heap->Entry[Hole].Key = Heap->Entry[Son].Key; - Heap->Entry[Hole].Data = Heap->Entry[Son].Data; - Hole = Son; - } - else - break; - } - Heap->Entry[Hole].Key = HoleKey; - Heap->Entry[Hole].Data = Heap->Entry[Heap->FirstFree].Data; - return (TESS_HEAP_OK); -} /* GetTopOfHeap */ - - -/*---------------------------------------------------------------------------*/ -/** - * This routine is similar to FreeHeap in that it - * deallocates the memory consumed by the heap. However, it - * also calls Deallocator for each item in the heap so that - * this data is also deallocated. - * - * @param Heap heap whose data is to be freed - * @param destructor function to be used to deallocate data - * - * Globals: - * - None - * - * @note Exceptions: none - * @note History: Tue May 15 08:52:04 1990, DSJ, Created. - */ -void FreeHeapData(HEAP *Heap, void_dest destructor) { - HEAPENTRY Entry; - - while (GetTopOfHeap (Heap, &Entry) != EMPTY) - destructor (Entry.Data); - - FreeHeap(Heap); -} /* FreeHeapData */ diff --git a/cutil/oldheap.h b/cutil/oldheap.h deleted file mode 100644 index da4974f473..0000000000 --- a/cutil/oldheap.h +++ /dev/null @@ -1,80 +0,0 @@ -/****************************************************************************** - ** Filename: heap.h - ** Purpose: Definition of heap access routines. - ** Author: Dan Johnson - ** History: 3/13/89, DSJ, Created. - ** - ** (c) Copyright Hewlett-Packard Company, 1988. - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** http://www.apache.org/licenses/LICENSE-2.0 - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - ******************************************************************************/ -#ifndef HEAP_H -#define HEAP_H - -/*----------------------------------------------------------------------------- - Include Files and Type Defines ------------------------------------------------------------------------------*/ -#include "host.h" -#include "cutil.h" - -#define HEAPFULL 3000 - -#define EMPTY -1 -#define TESS_HEAP_OK 0 - -struct HEAPENTRY { - FLOAT32 Key; - void *Data; -}; - -struct HEAP { - inT32 Size; - inT32 FirstFree; - HEAPENTRY Entry[1]; -}; - -/*----------------------------------------------------------------------------- - Macros ------------------------------------------------------------------------------*/ -#define FreeHeap(H) memfree(H) -#define MaxSizeOfHeap(H) (H->Size) -#define SizeOfHeap(H) (H->FirstFree - 1) -#define InitHeap(H) (H->FirstFree = 1) -#define HeapFull(H) ((H)->FirstFree > (H)->Size) -#define HeapEmpty(H) ((H)->FirstFree <= 1) - -/* macros for accessing elements in heap by index. The indicies vary from - 0 to SizeOfHeap-1. No bounds checking is done. Elements accessed in - this manner are in random order relative to the Key values. These - macros should never be used as the LHS of an assignment statement as this - will corrupt the heap.*/ -#define HeapKeyFor(H,E) ((H)->Entry[(E)+1].Key) -#define HeapDataFor(H,E) ((H)->Entry[(E)+1].Data) - -/*----------------------------------------------------------------------------- - Public Function Prototypes ------------------------------------------------------------------------------*/ -HEAP *MakeHeap(int Size); - -int HeapPop(HEAP *Heap, FLOAT32 *Key, void *out_ptr); - -int HeapPopWorst(HEAP *Heap, FLOAT32 *Key, void *out_ptr); - -void HeapPush(HEAP *Heap, FLOAT32 Key, void *Data); - -void HeapStore(HEAP *Heap, HEAPENTRY *Entry); - -int GetTopOfHeap(HEAP *Heap, HEAPENTRY *Entry); - -void FreeHeapData(HEAP *Heap, void_dest destructor); - -bool HeapPushCheckSize(HEAP *Heap, FLOAT32 Key, void *Data); - -#endif diff --git a/cutil/tessarray.cpp b/cutil/tessarray.cpp deleted file mode 100644 index 37b9b5246d..0000000000 --- a/cutil/tessarray.cpp +++ /dev/null @@ -1,115 +0,0 @@ -/* -*-C-*- -################################################################################ -# -# File: array.c -# Description: Dynamic Array of Strings -# Author: Mark Seaman, Software Productivity -# Created: Thu Jul 23 13:24:09 1987 -# Modified: Wed Mar 6 15:18:33 1991 (Mark Seaman) marks@hpgrlt -# Language: C -# Package: N/A -# Status: Reusable Software Component -# -# (c) Copyright 1987, Hewlett-Packard Company. -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** http://www.apache.org/licenses/LICENSE-2.0 -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -# -################################################################################ - -This file contains the implentations of a set of dynamic array of string -manipulation routines. For the interface definitions and documentation -of these routines see the file "das.h". - -***************************************************************************/ - -#include "tessarray.h" -#include "callcpp.h" -#include "freelist.h" - -#include -#include -#ifdef _WIN32 -#include -#endif -#include -#if MAC_OR_DOS -#include -#endif - -/********************************************************************** - * array_insert - * - * Insert a data element into a particular spot in the array. Move all - * the elements in the array (past that spot) down one to make room for - * the new element. - **********************************************************************/ -ARRAY array_insert(ARRAY array, int index, void *value) { - int x; - - array = array_push (array, NULL); - for (x = array_count (array) - 1; x > index; x--) - array_value (array, x) = array_value (array, x - 1); - array_value (array, index) = value; - return (array); -} - - -/********************************************************************** - * array_new - * - * Create a new array with a certain number of elements. If the number - * of elements requested is 0 then the default number will be used. - **********************************************************************/ -ARRAY array_new(int num) { - ARRAY temp; - int x; - - if (num == 0) - num = DEFAULT_SIZE; - temp = (ARRAY) memalloc ((num - 2) * sizeof (char *) + - sizeof (struct array_record)); - if (!temp) { - cprintf ("error: Out of memory in array_new\n"); - exit (1); //?err_exit (); - } - array_count (temp) = 0; - array_limit (temp) = num; - for (x = 0; x < num; x++) - array_value (temp, x) = (char *) 0; - return (temp); -} - - -/********************************************************************** - * array_push - * - * Add a new element onto the top of the array. If there is not room - * more room is made by "realloc"ing the array. This means that the - * new array location may change. All previous references to its old - * location may no longer be valid. - **********************************************************************/ -ARRAY array_push(ARRAY array, void *value) { - if (array_count (array) == array_limit (array)) { - array = (ARRAY) memrealloc (array, (array_limit (array) * 2 - 2) * - sizeof (char *) + - sizeof (struct array_record), - (array_limit (array) - - 2) * sizeof (char *) + - sizeof (struct array_record)); - if (!array) { - cprintf ("error: Out of memory in array_push\n"); - exit (1); //?err_exit (); - } - array_limit (array) *= 2; - } - array_count (array)++; - array_top (array) = value; - return (array); -} diff --git a/cutil/tessarray.h b/cutil/tessarray.h deleted file mode 100644 index a56dfcdbce..0000000000 --- a/cutil/tessarray.h +++ /dev/null @@ -1,166 +0,0 @@ -/* -*-C-*- - ******************************************************************************** - * - * File: array.h (Formerly array.h) - * Description: Dynamic Array of String - * Author: Mark Seaman, SW Productivity - * Created: Fri Oct 16 14:37:00 1987 - * Modified: Mon Sep 24 14:15:59 1990 (Mark Seaman) marks@hpgrlt - * Language: C - * Package: N/A - * Status: Reusable Software Component - * - * (c) Copyright 1987, Hewlett-Packard Company. - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** http://www.apache.org/licenses/LICENSE-2.0 - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - * - ***************************************************************************** - -This file contains a set of general purpose dynamic array of string routines. -These routines can be used in a wide variety of ways to provide several -different popular data structures. A new "das" can be created by declaring -a variable of type 'DAS' -******************************************************************************/ - -#ifndef TESSARRAY_H -#define TESSARRAY_H - -/* ----------------------------------------------------------------------- - I n c l u d e s ----------------------------------------------------------------------- -*/ - -#include - -/* ----------------------------------------------------------------------- - T y p e s ----------------------------------------------------------------------- -*/ - -typedef struct array_record -{ - size_t limit; - size_t top; - void *base[2]; -} *ARRAY; - -typedef void (*voidProc) (); - -typedef int (*intProc) (); - -/* ----------------------------------------------------------------------- - M a c r o s ----------------------------------------------------------------------- -*/ - -#define DEFAULT_SIZE 2 - -/********************************************************************** - * array_count - * - * Return the value of the number of elements currently in the array. - **********************************************************************/ - -#define array_count(a) \ -((a)->top) - -/********************************************************************** - * array_free - * - * Free the memory allocated to this array. - **********************************************************************/ - -#define array_free \ -memfree - -/********************************************************************** - * array_index - * - * Check to make sure that the index value is valid. Return the - * value of the nth element currently in the array. - **********************************************************************/ - -#define array_index(a,i) \ -((ibase[i] : 0) - -/********************************************************************** - * array_limit - * - * Return the maximum number of elements that could be currently held - * in this array without further expansion. - **********************************************************************/ - -#define array_limit(a) \ -((a)->limit) - -/********************************************************************** - * array_loop - * - * Iterate through each of the array elements. Each value can then be - * accessed by: - * array_index (a, x) - **********************************************************************/ - -#define array_loop(a,x) \ -for (x=0; x < array_count (a); x++) - -/********************************************************************** - * array_top - * - * Return the last element that was pushed on this array. - **********************************************************************/ - -#define array_top(a) \ -((a)->base[array_count (a) - 1]) - -/********************************************************************** - * array_value - * - * Return the nth element of the array. Don't do range checking. - **********************************************************************/ - -#define array_value(a,i) \ -((a)->base[i]) - -/*---------------------------------------------------------------------- - F u n c t i o n s -----------------------------------------------------------------------*/ -ARRAY array_insert(ARRAY array, int index, void *value); - -ARRAY array_new(int num); - -ARRAY array_push(ARRAY array, void *value); - -/* -#if defined(__STDC__) || defined(__cplusplus) -# define _ARGS(s) s -#else -# define _ARGS(s) () -#endif*/ - -/* array.c -ARRAY array_insert - _ARGS((ARRAY array, - int index, - char *value)); - -ARRAY array_new - _ARGS((int num)); - -ARRAY array_push - _ARGS((ARRAY array, - char *value)); - -#undef _ARGS -*/ -#endif diff --git a/dict/Makefile.am b/dict/Makefile.am index 44e41a5100..fcf58d119e 100644 --- a/dict/Makefile.am +++ b/dict/Makefile.am @@ -7,8 +7,8 @@ AM_CPPFLAGS += -DTESS_EXPORTS \ endif noinst_HEADERS = \ - dawg.h dict.h matchdefs.h \ - permute.h states.h stopper.h trie.h + dawg.h dawg_cache.h dict.h matchdefs.h \ + stopper.h trie.h if !USING_MULTIPLELIBS noinst_LTLIBRARIES = libtesseract_dict.la @@ -25,7 +25,7 @@ endif libtesseract_dict_la_SOURCES = \ context.cpp \ - dawg.cpp dict.cpp hyphen.cpp \ - permdawg.cpp permute.cpp states.cpp stopper.cpp trie.cpp + dawg.cpp dawg_cache.cpp dict.cpp hyphen.cpp \ + permdawg.cpp stopper.cpp trie.cpp diff --git a/dict/dawg.cpp b/dict/dawg.cpp index ed304245dc..74c26ba1ef 100644 --- a/dict/dawg.cpp +++ b/dict/dawg.cpp @@ -38,6 +38,7 @@ #include "freelist.h" #include "helpers.h" #include "strngs.h" +#include "tesscallback.h" #include "tprintf.h" /*---------------------------------------------------------------------- @@ -45,25 +46,29 @@ ----------------------------------------------------------------------*/ namespace tesseract { -bool Dawg::word_in_dawg(const WERD_CHOICE &word) const { - if (word.length() == 0) return false; +bool Dawg::prefix_in_dawg(const WERD_CHOICE &word, + bool requires_complete) const { + if (word.length() == 0) return !requires_complete; NODE_REF node = 0; int end_index = word.length() - 1; - for (int i = 0; i <= end_index; i++) { - if (debug_level_ > 1) { - tprintf("word_in_dawg: exploring node " REFFORMAT ":\n", node); - print_node(node, MAX_NODE_EDGES_DISPLAY); - tprintf("\n"); + for (int i = 0; i < end_index; i++) { + EDGE_REF edge = edge_char_of(node, word.unichar_id(i), false); + if (edge == NO_EDGE) { + return false; } - EDGE_REF edge = edge_char_of(node, word.unichar_id(i), i == end_index); - if (edge != NO_EDGE) { - node = next_node(edge); - if (node == 0) node = NO_EDGE; - } else { + if ((node = next_node(edge)) == 0) { + // This only happens if all words following this edge terminate -- + // there are no larger words. See Trie::add_word_to_dawg() return false; } } - return true; + // Now check the last character. + return edge_char_of(node, word.unichar_id(end_index), requires_complete) != + NO_EDGE; +} + +bool Dawg::word_in_dawg(const WERD_CHOICE &word) const { + return prefix_in_dawg(word, true); } int Dawg::check_for_words(const char *filename, @@ -99,23 +104,36 @@ int Dawg::check_for_words(const char *filename, } void Dawg::iterate_words(const UNICHARSET &unicharset, - TessCallback1 *cb) const { + TessCallback1 *cb) const { WERD_CHOICE word(&unicharset); iterate_words_rec(word, 0, cb); } +void CallWithUTF8(TessCallback1 *cb, const WERD_CHOICE *wc) { + STRING s; + wc->string_and_lengths(&s, NULL); + cb->Run(s.string()); +} + +void Dawg::iterate_words(const UNICHARSET &unicharset, + TessCallback1 *cb) const { + TessCallback1 *shim = + NewPermanentTessCallback(CallWithUTF8, cb); + WERD_CHOICE word(&unicharset); + iterate_words_rec(word, 0, shim); + delete shim; +} + void Dawg::iterate_words_rec(const WERD_CHOICE &word_so_far, NODE_REF to_explore, - TessCallback1 *cb) const { + TessCallback1 *cb) const { NodeChildVector children; - this->unichar_ids_of(to_explore, &children); + this->unichar_ids_of(to_explore, &children, false); for (int i = 0; i < children.size(); i++) { WERD_CHOICE next_word(word_so_far); next_word.append_unichar_id(children[i].unichar_id, 1, 0.0, 0.0); if (this->end_of_word(children[i].edge_ref)) { - STRING s; - next_word.string_and_lengths(&s, NULL); - cb->Run(s.string()); + cb->Run(&next_word); } NODE_REF next = next_node(children[i].edge_ref); if (next != 0) { @@ -132,7 +150,7 @@ bool Dawg::match_words(WERD_CHOICE *word, inT32 index, if (wildcard != INVALID_UNICHAR_ID && word->unichar_id(index) == wildcard) { bool any_matched = false; NodeChildVector vec; - this->unichar_ids_of(node, &vec); + this->unichar_ids_of(node, &vec, false); for (int i = 0; i < vec.size(); ++i) { word->set_unichar_id(vec[i].unichar_id, index); if (match_words(word, index, node, wildcard)) diff --git a/dict/dawg.h b/dict/dawg.h index f3b221d703..a487d3fd1c 100644 --- a/dict/dawg.h +++ b/dict/dawg.h @@ -91,10 +91,6 @@ enum DawgType { #define NUM_FLAG_BITS 3 #define REFFORMAT "%lld" -// Set kBeginningDawgsType[i] to true if a Dawg of -// DawgType i can contain the beginning of a word. -static const bool kBeginningDawgsType[] = { 1, 1, 1, 1 }; - static const bool kDawgSuccessors[DAWG_TYPE_COUNT][DAWG_TYPE_COUNT] = { { 0, 1, 1, 0 }, // for DAWG_TYPE_PUNCTUATION { 1, 0, 0, 0 }, // for DAWG_TYPE_WORD @@ -137,12 +133,21 @@ class Dawg { /// Returns true if the given word is in the Dawg. bool word_in_dawg(const WERD_CHOICE &word) const; + // Returns true if the given word prefix is not contraindicated by the dawg. + // If requires_complete is true, then the exact complete word must be present. + bool prefix_in_dawg(const WERD_CHOICE &prefix, bool requires_complete) const; + /// Checks the Dawg for the words that are listed in the requested file. /// Returns the number of words in the given file missing from the Dawg. int check_for_words(const char *filename, const UNICHARSET &unicharset, bool enable_wildcard) const; + // For each word in the Dawg, call the given (permanent) callback with the + // text (UTF-8) version of the word. + void iterate_words(const UNICHARSET &unicharset, + TessCallback1 *cb) const; + // For each word in the Dawg, call the given (permanent) callback with the // text (UTF-8) version of the word. void iterate_words(const UNICHARSET &unicharset, @@ -156,7 +161,8 @@ class Dawg { /// Fills the given NodeChildVector with all the unichar ids (and the /// corresponding EDGE_REFs) for which there is an edge out of this node. - virtual void unichar_ids_of(NODE_REF node, NodeChildVector *vec) const = 0; + virtual void unichar_ids_of(NODE_REF node, NodeChildVector *vec, + bool word_end) const = 0; /// Returns the next node visited by following the edge /// indicated by the given EDGE_REF. @@ -277,7 +283,7 @@ class Dawg { // Recursively iterate over all words in a dawg (see public iterate_words). void iterate_words_rec(const WERD_CHOICE &word_so_far, NODE_REF to_explore, - TessCallback1 *cb) const; + TessCallback1 *cb) const; // Member Variables. DawgType type_; @@ -299,22 +305,71 @@ class Dawg { }; // -/// DawgInfo struct and DawgInfoVector class are used for -/// storing information about the current Dawg search state. +// DawgPosition keeps track of where we are in the primary dawg we're searching +// as well as where we may be in the "punctuation dawg" which may provide +// surrounding context. +// +// Example: +// punctuation dawg -- space is the "pattern character" +// " " // no punctuation +// "' '" // leading and trailing apostrophes +// " '" // trailing apostrophe +// word dawg: +// "cat" +// "cab" +// "cat's" +// +// DawgPosition(dawg_index, dawg_ref, punc_index, punc_ref, rtp) // -struct DawgInfo { - DawgInfo() : dawg_index(-1), ref(NO_EDGE) {} - DawgInfo(int i, EDGE_REF r) : dawg_index(i), ref(r) {} - bool operator==(const DawgInfo &other) { - return (this->dawg_index == other.dawg_index && this->ref == other.ref); +// DawgPosition(-1, NO_EDGE, p, pe, false) +// We're in the punctuation dawg, no other dawg has been started. +// (1) If there's a pattern edge as a punc dawg child of us, +// for each punc-following dawg starting with ch, produce: +// Result: DawgPosition(k, w, p', false) +// (2) If there's a valid continuation in the punc dawg, produce: +// Result: DawgPosition(-k, NO_EDGE, p', false) +// +// DawgPosition(k, w, -1, NO_EDGE, false) +// We're in dawg k. Going back to punctuation dawg is not an option. +// Follow ch in dawg k. +// +// DawgPosition(k, w, p, pe, false) +// We're in dawg k. Continue in dawg k and/or go back to the punc dawg. +// If ending, check that the punctuation dawg is also ok to end here. +// +// DawgPosition(k, w, p, pe true) +// We're back in the punctuation dawg. Continuing there is the only option. +struct DawgPosition { + DawgPosition() + : dawg_index(-1), dawg_ref(NO_EDGE), punc_ref(NO_EDGE), + back_to_punc(false) {} + DawgPosition(int dawg_idx, EDGE_REF dawgref, + int punc_idx, EDGE_REF puncref, + bool backtopunc) + : dawg_index(dawg_idx), dawg_ref(dawgref), + punc_index(punc_idx), punc_ref(puncref), + back_to_punc(backtopunc) { + } + bool operator==(const DawgPosition &other) { + return dawg_index == other.dawg_index && + dawg_ref == other.dawg_ref && + punc_index == other.punc_index && + punc_ref == other.punc_ref && + back_to_punc == other.back_to_punc; } - int dawg_index; - EDGE_REF ref; + + inT8 dawg_index; + EDGE_REF dawg_ref; + inT8 punc_index; + EDGE_REF punc_ref; + // Have we returned to the punc dawg at the end of the word? + bool back_to_punc; }; -class DawgInfoVector : public GenericVector { + +class DawgPositionVector : public GenericVector { public: /// Overload destructor, since clear() does not delete data_[] any more. - ~DawgInfoVector() { + ~DawgPositionVector() { if (size_reserved_ > 0) { delete[] data_; size_used_ = 0; @@ -327,15 +382,17 @@ class DawgInfoVector : public GenericVector { /// Adds an entry for the given dawg_index with the given node to the vec. /// Returns false if the same entry already exists in the vector, /// true otherwise. - inline bool add_unique(const DawgInfo &new_info, bool debug, + inline bool add_unique(const DawgPosition &new_pos, + bool debug, const char *debug_msg) { for (int i = 0; i < size_used_; ++i) { - if (data_[i] == new_info) return false; + if (data_[i] == new_pos) return false; } - push_back(new_info); + push_back(new_pos); if (debug) { - tprintf("%s[%d, " REFFORMAT "]\n", debug_msg, - new_info.dawg_index, new_info.ref); + tprintf("%s[%d, " REFFORMAT "] [punc: " REFFORMAT "%s]\n", + debug_msg, new_pos.dawg_index, new_pos.dawg_ref, + new_pos.punc_ref, new_pos.back_to_punc ? " returned" : ""); } return true; } @@ -385,12 +442,15 @@ class SquishedDawg : public Dawg { /// Fills the given NodeChildVector with all the unichar ids (and the /// corresponding EDGE_REFs) for which there is an edge out of this node. - void unichar_ids_of(NODE_REF node, NodeChildVector *vec) const { + void unichar_ids_of(NODE_REF node, NodeChildVector *vec, + bool word_end) const { EDGE_REF edge = node; if (!edge_occupied(edge) || edge == NO_EDGE) return; assert(forward_edge(edge)); // we don't expect any backward edges to do { // be present when this funciton is called - vec->push_back(NodeChild(unichar_id_from_edge_rec(edges_[edge]), edge)); + if (!word_end || end_of_word_from_edge_rec(edges_[edge])) { + vec->push_back(NodeChild(unichar_id_from_edge_rec(edges_[edge]), edge)); + } } while (!last_edge(edge++)); } diff --git a/dict/dawg_cache.cpp b/dict/dawg_cache.cpp new file mode 100644 index 0000000000..4eda23a757 --- /dev/null +++ b/dict/dawg_cache.cpp @@ -0,0 +1,102 @@ +/////////////////////////////////////////////////////////////////////// +// File: dawg_cache.h +// Description: A class that knows about loading and caching dawgs. +// Author: David Eger +// Created: Fri Jan 27 12:08:00 PST 2012 +// +// (C) Copyright 2012, Google Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +/////////////////////////////////////////////////////////////////////// + +#include "dawg_cache.h" + +#include "dawg.h" +#include "object_cache.h" +#include "strngs.h" +#include "tessdatamanager.h" + +namespace tesseract { + +struct DawgLoader { + DawgLoader(const STRING &lang, + const char *data_file_name, + TessdataType tessdata_dawg_type, + int dawg_debug_level) + : lang_(lang), + data_file_name_(data_file_name), + tessdata_dawg_type_(tessdata_dawg_type), + dawg_debug_level_(dawg_debug_level) {} + + Dawg *Load(); + + STRING lang_; + const char *data_file_name_; + TessdataType tessdata_dawg_type_; + int dawg_debug_level_; +}; + +Dawg *DawgCache::GetSquishedDawg( + const STRING &lang, + const char *data_file_name, + TessdataType tessdata_dawg_type, + int debug_level) { + STRING data_id = data_file_name; + data_id += kTessdataFileSuffixes[tessdata_dawg_type]; + DawgLoader loader(lang, data_file_name, tessdata_dawg_type, debug_level); + return dawgs_.Get(data_id, NewTessCallback(&loader, &DawgLoader::Load)); +} + +Dawg *DawgLoader::Load() { + TessdataManager data_loader; + if (!data_loader.Init(data_file_name_, dawg_debug_level_)) { + return NULL; + } + if (!data_loader.SeekToStart(tessdata_dawg_type_)) return NULL; + FILE *fp = data_loader.GetDataFilePtr(); + DawgType dawg_type; + PermuterType perm_type; + switch (tessdata_dawg_type_) { + case TESSDATA_PUNC_DAWG: + dawg_type = DAWG_TYPE_PUNCTUATION; + perm_type = PUNC_PERM; + break; + case TESSDATA_SYSTEM_DAWG: + dawg_type = DAWG_TYPE_WORD; + perm_type = SYSTEM_DAWG_PERM; + break; + case TESSDATA_NUMBER_DAWG: + dawg_type = DAWG_TYPE_NUMBER; + perm_type = NUMBER_PERM; + break; + case TESSDATA_BIGRAM_DAWG: + dawg_type = DAWG_TYPE_WORD; // doesn't actually matter + perm_type = COMPOUND_PERM; // doesn't actually matter + break; + case TESSDATA_UNAMBIG_DAWG: + dawg_type = DAWG_TYPE_WORD; + perm_type = SYSTEM_DAWG_PERM; + break; + case TESSDATA_FREQ_DAWG: + dawg_type = DAWG_TYPE_WORD; + perm_type = FREQ_DAWG_PERM; + break; + default: + data_loader.End(); + return NULL; + } + SquishedDawg *retval = + new SquishedDawg(fp, dawg_type, lang_, perm_type, dawg_debug_level_); + data_loader.End(); + return retval; +} + +} // namespace tesseract diff --git a/dict/dawg_cache.h b/dict/dawg_cache.h new file mode 100644 index 0000000000..cd1721a77d --- /dev/null +++ b/dict/dawg_cache.h @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////// +// File: dawg_cache.h +// Description: A class that knows about loading and caching dawgs. +// Author: David Eger +// Created: Fri Jan 27 12:08:00 PST 2012 +// +// (C) Copyright 2012, Google Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +/////////////////////////////////////////////////////////////////////// + +#ifndef TESSERACT_DICT_DAWG_CACHE_H_ +#define TESSERACT_DICT_DAWG_CACHE_H_ + +#include "dawg.h" +#include "object_cache.h" +#include "strngs.h" +#include "tessdatamanager.h" + +namespace tesseract { + +class DawgCache { + public: + Dawg *GetSquishedDawg( + const STRING &lang, + const char *data_file_name, + TessdataType tessdata_dawg_type, + int debug_level); + + // If we manage the given dawg, decrement its count, + // and possibly delete it if the count reaches zero. + // If dawg is unknown to us, return false. + bool FreeDawg(Dawg *dawg) { + return dawgs_.Free(dawg); + } + + // Free up any currently unused dawgs. + void DeleteUnusedDawgs() { + dawgs_.DeleteUnusedObjects(); + } + + private: + ObjectCache dawgs_; +}; + +} // namespace tesseract + +#endif // TESSERACT_DICT_DAWG_CACHE_H_ diff --git a/dict/dict.cpp b/dict/dict.cpp index b9a0bc701b..a21da79672 100644 --- a/dict/dict.cpp +++ b/dict/dict.cpp @@ -33,6 +33,7 @@ class Image; Dict::Dict(Image* image_ptr) : letter_is_okay_(&tesseract::Dict::def_letter_is_okay), probability_in_context_(&tesseract::Dict::def_probability_in_context), + params_model_classify_(NULL), image_ptr_(image_ptr), STRING_INIT_MEMBER(user_words_suffix, "", "A list of user-provided words.", @@ -50,11 +51,15 @@ Dict::Dict(Image* image_ptr) " patterns.", getImage()->getCCUtil()->params()), BOOL_INIT_MEMBER(load_number_dawg, true, "Load dawg with number" " patterns.", getImage()->getCCUtil()->params()), - BOOL_INIT_MEMBER(load_fixed_length_dawgs, true, "Load fixed length dawgs" - " (e.g. for non-space delimited languages)", - getImage()->getCCUtil()->params()), - BOOL_INIT_MEMBER(load_bigram_dawg, false, "Load dawg with special word " + BOOL_INIT_MEMBER(load_bigram_dawg, true, "Load dawg with special word " "bigrams.", getImage()->getCCUtil()->params()), + double_MEMBER(xheight_penalty_subscripts, 0.125, + "Score penalty (0.1 = 10%) added if there are subscripts " + "or superscripts in a word, but it is otherwise OK.", + getImage()->getCCUtil()->params()), + double_MEMBER(xheight_penalty_inconsistent, 0.25, + "Score penalty (0.1 = 10%) added if an xheight is " + "inconsistent.", getImage()->getCCUtil()->params()), double_MEMBER(segment_penalty_dict_frequent_word, 1.0, "Score multiplier for word matches which have good case and" "are frequent in the given language (lower is better).", @@ -114,14 +119,6 @@ Dict::Dict(Image* image_ptr) "Make AcceptableChoice() always return false. Useful" " when there is a need to explore all segmentations", getImage()->getCCUtil()->params()), - double_MEMBER(stopper_ambiguity_threshold_gain, 8.0, - "Gain factor for ambiguity threshold.", - getImage()->getCCUtil()->params()), - double_MEMBER(stopper_ambiguity_threshold_offset, 1.5, - "Certainty offset for ambiguity threshold.", - getImage()->getCCUtil()->params()), - BOOL_MEMBER(save_raw_choices, false, "Save all explored raw choices", - getImage()->getCCUtil()->params()), INT_MEMBER(tessedit_truncate_wordchoice_log, 10, "Max words to keep in list", getImage()->getCCUtil()->params()), @@ -133,80 +130,40 @@ Dict::Dict(Image* image_ptr) getImage()->getCCUtil()->params()), INT_MEMBER(fragments_debug, 0, "Debug character fragments", getImage()->getCCUtil()->params()), - INT_MEMBER(segment_debug, 0, "Debug the whole segmentation process", - getImage()->getCCUtil()->params()), - BOOL_MEMBER(permute_debug, 0, "Debug char permutation process", - getImage()->getCCUtil()->params()), - double_MEMBER(bestrate_pruning_factor, 2.0, "Multiplying factor of" - " current best rate to prune other hypotheses", - getImage()->getCCUtil()->params()), - BOOL_MEMBER(permute_script_word, 0, - "Turn on word script consistency permuter", - getImage()->getCCUtil()->params()), - BOOL_MEMBER(segment_segcost_rating, 0, - "incorporate segmentation cost in word rating?", - getImage()->getCCUtil()->params()), BOOL_MEMBER(segment_nonalphabetic_script, false, "Don't use any alphabetic-specific tricks." "Set to true in the traineddata config file for" " scripts that are cursive or inherently fixed-pitch", getImage()->getCCUtil()->params()), - double_MEMBER(segment_reward_script, 0.95, - "Score multipler for script consistency within a word. " - "Being a 'reward' factor, it should be <= 1. " - "Smaller value implies bigger reward.", - getImage()->getCCUtil()->params()), - BOOL_MEMBER(permute_fixed_length_dawg, 0, - "Turn on fixed-length phrasebook search permuter", - getImage()->getCCUtil()->params()), - BOOL_MEMBER(permute_chartype_word, 0, - "Turn on character type (property) consistency permuter", - getImage()->getCCUtil()->params()), - double_MEMBER(segment_reward_chartype, 0.97, - "Score multipler for char type consistency within a word. ", - getImage()->getCCUtil()->params()), - double_MEMBER(segment_reward_ngram_best_choice, 0.99, - "Score multipler for ngram permuter's best choice" - " (only used in the Han script path).", - getImage()->getCCUtil()->params()), BOOL_MEMBER(save_doc_words, 0, "Save Document Words", getImage()->getCCUtil()->params()), - BOOL_MEMBER(doc_dict_enable, 1, "Enable Document Dictionary ", - getImage()->getCCUtil()->params()), double_MEMBER(doc_dict_pending_threshold, 0.0, "Worst certainty for using pending dictionary", getImage()->getCCUtil()->params()), double_MEMBER(doc_dict_certainty_threshold, -2.25, "Worst certainty for words that can be inserted into the" "document dictionary", getImage()->getCCUtil()->params()), - BOOL_MEMBER(ngram_permuter_activated, false, - "Activate character-level n-gram-based permuter", - getImage()->getCCUtil()->params()), INT_MEMBER(max_permuter_attempts, 10000, "Maximum number of different" " character choices to consider during permutation." " This limit is especially useful when user patterns" " are specified, since overly generic patterns can result in" " dawg search exploring an overly large number of options.", - getImage()->getCCUtil()->params()), - BOOL_MEMBER(permute_only_top, false, "Run only the top choice permuter", - getImage()->getCCUtil()->params()) { + getImage()->getCCUtil()->params()) { dang_ambigs_table_ = NULL; replace_ambigs_table_ = NULL; - keep_word_choices_ = false; reject_offset_ = 0.0; - best_raw_choice_ = NULL; - best_choices_ = NIL_LIST; - raw_choices_ = NIL_LIST; go_deeper_fxn_ = NULL; hyphen_word_ = NULL; last_word_on_line_ = false; hyphen_unichar_id_ = INVALID_UNICHAR_ID; document_words_ = NULL; + dawg_cache_ = NULL; + dawg_cache_is_ours_ = false; pending_words_ = NULL; bigram_dawg_ = NULL; freq_dawg_ = NULL; punc_dawg_ = NULL; - max_fixed_length_dawgs_wdlen_ = -1; + unambig_dawg_ = NULL; wordseg_rating_adjust_factor_ = -1.0f; output_ambig_words_file_ = NULL; } @@ -216,56 +173,65 @@ Dict::~Dict() { if (output_ambig_words_file_ != NULL) fclose(output_ambig_words_file_); } -void Dict::Load() { +DawgCache *Dict::GlobalDawgCache() { + // We dynamically allocate this global cache (a singleton) so it will outlive + // every Tesseract instance (even those that someone else might declare as + // global statics). + static DawgCache *cache = new DawgCache(); // evil global singleton + return cache; +} + +void Dict::Load(DawgCache *dawg_cache) { STRING name; STRING &lang = getImage()->getCCUtil()->lang; if (dawgs_.length() != 0) this->End(); + apostrophe_unichar_id_ = getUnicharset().unichar_to_id(kApostropheSymbol); + question_unichar_id_ = getUnicharset().unichar_to_id(kQuestionSymbol); + slash_unichar_id_ = getUnicharset().unichar_to_id(kSlashSymbol); hyphen_unichar_id_ = getUnicharset().unichar_to_id(kHyphenSymbol); - LoadEquivalenceList(kHyphenLikeUTF8); - LoadEquivalenceList(kApostropheLikeUTF8); + if (dawg_cache != NULL) { + dawg_cache_ = dawg_cache; + dawg_cache_is_ours_ = false; + } else { + dawg_cache_ = new DawgCache(); + dawg_cache_is_ours_ = true; + } - TessdataManager &tessdata_manager = - getImage()->getCCUtil()->tessdata_manager; + TessdataManager &tessdata_manager = getImage()->getCCUtil()->tessdata_manager; + const char *data_file_name = tessdata_manager.GetDataFileName().string(); // Load dawgs_. - if (load_punc_dawg && tessdata_manager.SeekToStart(TESSDATA_PUNC_DAWG)) { - punc_dawg_ = new SquishedDawg(tessdata_manager.GetDataFilePtr(), - DAWG_TYPE_PUNCTUATION, lang, PUNC_PERM, - dawg_debug_level); - dawgs_ += punc_dawg_; - } - if (load_system_dawg && tessdata_manager.SeekToStart(TESSDATA_SYSTEM_DAWG)) { - dawgs_ += new SquishedDawg(tessdata_manager.GetDataFilePtr(), - DAWG_TYPE_WORD, lang, SYSTEM_DAWG_PERM, - dawg_debug_level); - } - if (load_number_dawg && tessdata_manager.SeekToStart(TESSDATA_NUMBER_DAWG)) { - dawgs_ += - new SquishedDawg(tessdata_manager.GetDataFilePtr(), - DAWG_TYPE_NUMBER, lang, NUMBER_PERM, dawg_debug_level); - } - if (load_bigram_dawg && tessdata_manager.SeekToStart(TESSDATA_BIGRAM_DAWG)) { - bigram_dawg_ = new SquishedDawg(tessdata_manager.GetDataFilePtr(), - DAWG_TYPE_WORD, // doesn't actually matter. - lang, - COMPOUND_PERM, // doesn't actually matter. - dawg_debug_level); - } - if (load_freq_dawg && tessdata_manager.SeekToStart(TESSDATA_FREQ_DAWG)) { - freq_dawg_ = new SquishedDawg(tessdata_manager.GetDataFilePtr(), - DAWG_TYPE_WORD, lang, FREQ_DAWG_PERM, - dawg_debug_level); - dawgs_ += freq_dawg_; - } - if (load_unambig_dawg && - tessdata_manager.SeekToStart(TESSDATA_UNAMBIG_DAWG)) { - unambig_dawg_ = new SquishedDawg(tessdata_manager.GetDataFilePtr(), - DAWG_TYPE_WORD, lang, SYSTEM_DAWG_PERM, - dawg_debug_level); - dawgs_ += unambig_dawg_; + if (load_punc_dawg) { + punc_dawg_ = dawg_cache_->GetSquishedDawg( + lang, data_file_name, TESSDATA_PUNC_DAWG, dawg_debug_level); + if (punc_dawg_) dawgs_ += punc_dawg_; + } + if (load_system_dawg) { + Dawg *system_dawg = dawg_cache_->GetSquishedDawg( + lang, data_file_name, TESSDATA_SYSTEM_DAWG, dawg_debug_level); + if (system_dawg) dawgs_ += system_dawg; + } + if (load_number_dawg) { + Dawg *number_dawg = dawg_cache_->GetSquishedDawg( + lang, data_file_name, TESSDATA_NUMBER_DAWG, dawg_debug_level); + if (number_dawg) dawgs_ += number_dawg; + } + if (load_bigram_dawg) { + bigram_dawg_ = dawg_cache_->GetSquishedDawg( + lang, data_file_name, TESSDATA_BIGRAM_DAWG, dawg_debug_level); + } + if (load_freq_dawg) { + freq_dawg_ = dawg_cache_->GetSquishedDawg( + lang, data_file_name, TESSDATA_FREQ_DAWG, dawg_debug_level); + if (freq_dawg_) { dawgs_ += freq_dawg_; } + } + if (load_unambig_dawg) { + unambig_dawg_ = dawg_cache_->GetSquishedDawg( + lang, data_file_name, TESSDATA_UNAMBIG_DAWG, dawg_debug_level); + if (unambig_dawg_) dawgs_ += unambig_dawg_; } if (((STRING &)user_words_suffix).length() > 0) { @@ -274,12 +240,13 @@ void Dict::Load() { dawg_debug_level); name = getImage()->getCCUtil()->language_data_path_prefix; name += user_words_suffix; - if (!trie_ptr->read_word_list(name.string(), getUnicharset(), - Trie::RRP_REVERSE_IF_HAS_RTL)) { + if (!trie_ptr->read_and_add_word_list(name.string(), getUnicharset(), + Trie::RRP_REVERSE_IF_HAS_RTL)) { tprintf("Error: failed to load %s\n", name.string()); - exit(1); + delete trie_ptr; + } else { + dawgs_ += trie_ptr; } - dawgs_ += trie_ptr; } if (((STRING &)user_patterns_suffix).length() > 0) { @@ -291,9 +258,10 @@ void Dict::Load() { name += user_patterns_suffix; if (!trie_ptr->read_pattern_list(name.string(), getUnicharset())) { tprintf("Error: failed to load %s\n", name.string()); - exit(1); + delete trie_ptr; + } else { + dawgs_ += trie_ptr; } - dawgs_ += trie_ptr; } document_words_ = new Trie(DAWG_TYPE_WORD, lang, DOC_DAWG_PERM, @@ -306,15 +274,6 @@ void Dict::Load() { kMaxDocDawgEdges, getUnicharset().size(), dawg_debug_level); - // Load fixed length dawgs if necessary (used for phrase search - // for non-space delimited languages). - if (load_fixed_length_dawgs && - tessdata_manager.SeekToStart(TESSDATA_FIXED_LENGTH_DAWGS)) { - ReadFixedLengthDawgs(DAWG_TYPE_WORD, lang, SYSTEM_DAWG_PERM, - dawg_debug_level, tessdata_manager.GetDataFilePtr(), - &dawgs_, &max_fixed_length_dawgs_wdlen_); - } - // Construct a list of corresponding successors for each dawg. Each entry i // in the successors_ vector is a vector of integers that represent the // indices into the dawgs_ vector of the successors for dawg i. @@ -335,45 +294,26 @@ void Dict::Load() { void Dict::End() { if (dawgs_.length() == 0) return; // Not safe to call twice. - dawgs_.delete_data_pointers(); + for (int i = 0; i < dawgs_.size(); i++) { + if (!dawg_cache_->FreeDawg(dawgs_[i])) { + delete dawgs_[i]; + } + } + dawg_cache_->FreeDawg(bigram_dawg_); + if (dawg_cache_is_ours_) { + delete dawg_cache_; + dawg_cache_ = NULL; + } successors_.delete_data_pointers(); dawgs_.clear(); - delete bigram_dawg_; successors_.clear(); document_words_ = NULL; - max_fixed_length_dawgs_wdlen_ = -1; if (pending_words_ != NULL) { delete pending_words_; pending_words_ = NULL; } } -// Create unicharset adaptations of known, short lists of UTF-8 equivalent -// characters (think all hyphen-like symbols). The first version of the -// list is taken as equivalent for matching against the dictionary. -void Dict::LoadEquivalenceList(const char *unichar_strings[]) { - equivalent_symbols_.push_back(GenericVectorEqEq()); - const UNICHARSET &unicharset = getUnicharset(); - GenericVectorEqEq *equiv_list = &equivalent_symbols_.back(); - for (int i = 0; unichar_strings[i] != 0; i++) { - UNICHAR_ID unichar_id = unicharset.unichar_to_id(unichar_strings[i]); - if (unichar_id != INVALID_UNICHAR_ID) { - equiv_list->push_back(unichar_id); - } - } -} - -// Normalize all hyphen and apostrophes to the canonicalized one for -// matching; pass everything else through as is. -UNICHAR_ID Dict::NormalizeUnicharIdForMatch(UNICHAR_ID unichar_id) const { - for (int i = 0; i < equivalent_symbols_.size(); i++) { - if (equivalent_symbols_[i].contains(unichar_id)) { - return equivalent_symbols_[i][0]; - } - } - return unichar_id; -} - // Returns true if in light of the current state unichar_id is allowed // according to at least one of the dawgs in the dawgs_ vector. // See more extensive comments in dict.h where this function is declared. @@ -384,10 +324,9 @@ int Dict::def_letter_is_okay(void* void_dawg_args, if (dawg_debug_level >= 3) { tprintf("def_letter_is_okay: current unichar=%s word_end=%d" - " num active dawgs=%d num constraints=%d\n", + " num active dawgs=%d\n", getUnicharset().debug_str(unichar_id).string(), word_end, - dawg_args->active_dawgs->length(), - dawg_args->constraints->length()); + dawg_args->active_dawgs->length()); } // Do not accept words that contain kPatternUnicharID. @@ -401,144 +340,118 @@ int Dict::def_letter_is_okay(void* void_dawg_args, // Initialization. PermuterType curr_perm = NO_PERM; - dawg_args->updated_active_dawgs->clear(); - const DawgInfoVector &constraints = *(dawg_args->constraints); - *dawg_args->updated_constraints = constraints; + dawg_args->updated_dawgs->clear(); - // Go over the active_dawgs vector and insert DawgInfo records with the - // updated ref (an edge with the corresponding unichar id) into - // dawg_args->updated_active_dawgs. + // Go over the active_dawgs vector and insert DawgPosition records + // with the updated ref (an edge with the corresponding unichar id) into + // dawg_args->updated_pos. for (int a = 0; a < dawg_args->active_dawgs->length(); ++a) { - const DawgInfo &info = (*dawg_args->active_dawgs)[a]; - const Dawg *dawg = dawgs_[info.dawg_index]; - // dawg_unichar_id will contain the literal unichar_id to be found in the - // dawgs (e.g. didgit pattern if unichar_id is a digit and dawg contains - // number patterns, word pattern if dawg is a puncutation dawg and we - // reached an end of beginning puntuation pattern, etc). - UNICHAR_ID dawg_unichar_id = unichar_id; + const DawgPosition &pos = (*dawg_args->active_dawgs)[a]; + const Dawg *punc_dawg = pos.punc_index >= 0 ? dawgs_[pos.punc_index] : NULL; + const Dawg *dawg = pos.dawg_index >= 0 ? dawgs_[pos.dawg_index] : NULL; + + if (!dawg && !punc_dawg) { + // shouldn't happen. + tprintf("Received DawgPosition with no dawg or punc_dawg. wth?\n"); + continue; + } + if (!dawg) { + // We're in the punctuation dawg. A core dawg has not been chosen. + NODE_REF punc_node = GetStartingNode(punc_dawg, pos.punc_ref); + EDGE_REF punc_transition_edge = punc_dawg->edge_char_of( + punc_node, Dawg::kPatternUnicharID, word_end); + if (punc_transition_edge != NO_EDGE) { + // Find all successors, and see which can transition. + const SuccessorList &slist = *(successors_[pos.punc_index]); + for (int s = 0; s < slist.length(); ++s) { + int sdawg_index = slist[s]; + const Dawg *sdawg = dawgs_[sdawg_index]; + UNICHAR_ID ch = char_for_dawg(unichar_id, sdawg); + EDGE_REF dawg_edge = sdawg->edge_char_of(0, ch, word_end); + if (dawg_edge != NO_EDGE) { + if (dawg_debug_level >=3) { + tprintf("Letter found in dawg %d\n", sdawg_index); + } + dawg_args->updated_dawgs->add_unique( + DawgPosition(sdawg_index, dawg_edge, + pos.punc_index, punc_transition_edge, false), + dawg_debug_level > 0, + "Append transition from punc dawg to current dawgs: "); + if (sdawg->permuter() > curr_perm) curr_perm = sdawg->permuter(); + } + } + } + EDGE_REF punc_edge = punc_dawg->edge_char_of(punc_node, unichar_id, + word_end); + if (punc_edge != NO_EDGE) { + if (dawg_debug_level >=3) { + tprintf("Letter found in punctuation dawg\n"); + } + dawg_args->updated_dawgs->add_unique( + DawgPosition(-1, NO_EDGE, pos.punc_index, punc_edge, false), + dawg_debug_level > 0, + "Extend punctuation dawg: "); + if (PUNC_PERM > curr_perm) curr_perm = PUNC_PERM; + } + continue; + } + + if (punc_dawg && dawg->end_of_word(pos.dawg_ref)) { + // We can end the main word here. + // If we can continue on the punc ref, add that possibility. + NODE_REF punc_node = GetStartingNode(punc_dawg, pos.punc_ref); + EDGE_REF punc_edge = punc_node == NO_EDGE ? NO_EDGE + : punc_dawg->edge_char_of(punc_node, unichar_id, word_end); + if (punc_edge != NO_EDGE) { + dawg_args->updated_dawgs->add_unique( + DawgPosition(pos.dawg_index, pos.dawg_ref, + pos.punc_index, punc_edge, true), + dawg_debug_level > 0, + "Return to punctuation dawg: "); + if (dawg->permuter() > curr_perm) curr_perm = dawg->permuter(); + } + } + + if (pos.back_to_punc) continue; // If we are dealing with the pattern dawg, look up all the // possible edges, not only for the exact unichar_id, but also // for all its character classes (alpha, digit, etc). if (dawg->type() == DAWG_TYPE_PATTERN) { - ProcessPatternEdges(dawg, info, dawg_unichar_id, word_end, - dawg_args, &curr_perm); + ProcessPatternEdges(dawg, pos, unichar_id, word_end, + dawg_args->updated_dawgs, &curr_perm); // There can't be any successors to dawg that is of type - // DAWG_TYPE_PATTERN, so we are done examining this DawgInfo. + // DAWG_TYPE_PATTERN, so we are done examining this DawgPosition. continue; } - // The number dawg generalizes all digits to be kPatternUnicharID, - // so try to match kPatternUnicharID if the current unichar is a digit. - if (dawg->type() == DAWG_TYPE_NUMBER && - getUnicharset().get_isdigit(dawg_unichar_id)) { - dawg_unichar_id = Dawg::kPatternUnicharID; - } - - // Find the edge out of the node for the dawg_unichar_id. - NODE_REF node = GetStartingNode(dawg, info.ref); - EDGE_REF edge = (node != NO_EDGE) ? - dawg->edge_char_of(node, dawg_unichar_id, word_end) : NO_EDGE; + // Find the edge out of the node for the unichar_id. + NODE_REF node = GetStartingNode(dawg, pos.dawg_ref); + EDGE_REF edge = (node == NO_EDGE) ? NO_EDGE + : dawg->edge_char_of(node, char_for_dawg(unichar_id, dawg), word_end); if (dawg_debug_level >= 3) { tprintf("Active dawg: [%d, " REFFORMAT "] edge=" REFFORMAT "\n", - info.dawg_index, node, edge); + pos.dawg_index, node, edge); } if (edge != NO_EDGE) { // the unichar was found in the current dawg - if (ConstraintsOk(*(dawg_args->updated_constraints), - word_end, dawg->type())) { - if (dawg_debug_level >=3) { - tprintf("Letter found in dawg %d\n", info.dawg_index); - } - if (dawg->permuter() > curr_perm) curr_perm = dawg->permuter(); - dawg_args->updated_active_dawgs->add_unique( - DawgInfo(info.dawg_index, edge), dawg_debug_level > 0, - "Append current dawg to updated active dawgs: "); - } - } else if (dawg_args->sought_word_length == kAnyWordLength) { - // The unichar was not found in the current dawg. - // Explore the successor dawgs (but only if we are not - // just searching one dawg with a fixed word length). - - // Handle leading/trailing punctuation dawgs that denote a word pattern - // as an edge with kPatternUnicharID. If such an edge is found we add a - // constraint denoting the state of the dawg before the word pattern. - // This constraint will be applied later when this dawg is found among - // successor dawgs as well potentially at the end of the word. - if (dawg->type() == DAWG_TYPE_PUNCTUATION) { - edge = dawg->edge_char_of(node, Dawg::kPatternUnicharID, word_end); - if (edge != NO_EDGE) { - dawg_args->updated_constraints->add_unique( - DawgInfo(info.dawg_index, edge), dawg_debug_level > 0, - "Recording constraint: "); - } else { - // Do not explore successors of this dawg, since this - // must be invalid leading or trailing punctuation. - if (dawg_debug_level >= 3) { - tprintf("Invalid punctuation from dawg %d\n", info.dawg_index); - } - continue; - } - } - - if (info.ref == NO_EDGE) { - if (dawg_debug_level >= 3) { - tprintf("No letters matched in dawg %d\n", info.dawg_index); - } - continue; + if (dawg_debug_level >=3) { + tprintf("Letter found in dawg %d\n", pos.dawg_index); } - - // Discard the dawg if the pattern can not end at previous letter. - if (edge == NO_EDGE && // previous part is not leading punctuation - !dawg->end_of_word(info.ref)) { + if (word_end && punc_dawg && !punc_dawg->end_of_word(pos.punc_ref)) { if (dawg_debug_level >= 3) { - tprintf("No valid pattern end in dawg %d\n", info.dawg_index); + tprintf("Punctuation constraint not satisfied at end of word.\n"); } continue; } - - // Look for the unichar in each of this dawg's successors - // and append those in which it is found to active_dawgs. - const SuccessorList &slist = *(successors_[info.dawg_index]); - for (int s = 0; s < slist.length(); ++s) { - int sdawg_index = slist[s]; - const Dawg *sdawg = dawgs_[sdawg_index]; - NODE_REF snode = 0; - // Apply constraints to the successor dawg. - for (int c = 0; c < constraints.length(); ++c) { - // If the successor dawg is described in the constraints change - // the start ref from 0 to the one recorded as the constraint. - const DawgInfo &cinfo = constraints[c]; - if (cinfo.dawg_index == sdawg_index) { - snode = sdawg->next_node(cinfo.ref); - // Make sure we do not search the successor dawg if after - // applying the saved constraint we are at the end of the word. - if (snode == 0) snode = NO_EDGE; - if (dawg_debug_level >= 3) { - tprintf("Applying constraint [%d, " REFFORMAT "]\n", - sdawg_index, snode); - } - } - } - // Look for the letter in this successor dawg. - EDGE_REF sedge = sdawg->edge_char_of(snode, unichar_id, word_end); - // If we found the letter append sdawg to the active_dawgs list. - if (sedge != NO_EDGE && - ConstraintsOk(*(dawg_args->updated_constraints), word_end, - dawgs_[sdawg_index]->type())) { - if (dawg_debug_level >= 3) { - tprintf("Letter found in the successor dawg %d\n", sdawg_index); - } - if (sdawg->permuter() > curr_perm) curr_perm = sdawg->permuter(); - if (sdawg->next_node(sedge) != 0) { // if not word end - dawg_args->updated_active_dawgs->add_unique( - DawgInfo(sdawg_index, sedge), dawg_debug_level > 0, - "Append successor to updated active dawgs: "); - } - } - } // end successors loop - } // end if/else + if (dawg->permuter() > curr_perm) curr_perm = dawg->permuter(); + dawg_args->updated_dawgs->add_unique( + DawgPosition(pos.dawg_index, edge, pos.punc_index, pos.punc_ref, + false), + dawg_debug_level > 0, + "Append current dawg to updated active dawgs: "); + } } // end for // Update dawg_args->permuter if it used to be NO_PERM or became NO_PERM // or if we found the current letter in a non-punctuation dawg. This @@ -548,14 +461,17 @@ int Dict::def_letter_is_okay(void* void_dawg_args, (curr_perm != PUNC_PERM && dawg_args->permuter != COMPOUND_PERM)) { dawg_args->permuter = curr_perm; } + if (dawg_debug_level >= 2) { + tprintf("Returning %d for permuter code for this character.\n"); + } return dawg_args->permuter; } -void Dict::ProcessPatternEdges(const Dawg *dawg, const DawgInfo &info, +void Dict::ProcessPatternEdges(const Dawg *dawg, const DawgPosition &pos, UNICHAR_ID unichar_id, bool word_end, - DawgArgs *dawg_args, + DawgPositionVector *updated_dawgs, PermuterType *curr_perm) const { - NODE_REF node = GetStartingNode(dawg, info.ref); + NODE_REF node = GetStartingNode(dawg, pos.dawg_ref); // Try to find the edge corresponding to the exact unichar_id and to all the // edges corresponding to the character class of unichar_id. GenericVector unichar_id_patterns; @@ -566,73 +482,21 @@ void Dict::ProcessPatternEdges(const Dawg *dawg, const DawgInfo &info, // On the first iteration check all the outgoing edges. // On the second iteration check all self-loops. for (int k = 0; k < 2; ++k) { - EDGE_REF edge = (k == 0) ? - dawg->edge_char_of(node, unichar_id_patterns[i], word_end) - : dawg->pattern_loop_edge(info.ref, unichar_id_patterns[i], word_end); - if (edge != NO_EDGE) { - if (dawg_debug_level >= 3) { - tprintf("Pattern dawg: [%d, " REFFORMAT "] edge=" REFFORMAT "\n", - info.dawg_index, node, edge); - } - if (ConstraintsOk(*(dawg_args->updated_constraints), - word_end, dawg->type())) { - if (dawg_debug_level >=3) { - tprintf("Letter found in pattern dawg %d\n", info.dawg_index); - } - if (dawg->permuter() > *curr_perm) *curr_perm = dawg->permuter(); - dawg_args->updated_active_dawgs->add_unique( - DawgInfo(info.dawg_index, edge), dawg_debug_level > 0, - "Append current dawg to updated active dawgs: "); - } + EDGE_REF edge = (k == 0) + ? dawg->edge_char_of(node, unichar_id_patterns[i], word_end) + : dawg->pattern_loop_edge(pos.dawg_ref, unichar_id_patterns[i], word_end); + if (edge == NO_EDGE) continue; + if (dawg_debug_level >= 3) { + tprintf("Pattern dawg: [%d, " REFFORMAT "] edge=" REFFORMAT "\n", + pos.dawg_index, node, edge); + tprintf("Letter found in pattern dawg %d\n", pos.dawg_index); } - } - } -} - -void Dict::ReadFixedLengthDawgs(DawgType type, const STRING &lang, - PermuterType perm, int debug_level, - FILE *file, DawgVector *dawg_vec, - int *max_wdlen) { - int i; - DawgVector dawg_vec_copy; - dawg_vec_copy.move(dawg_vec); // save the input dawg_vec. - inT32 num_dawgs; - fread(&num_dawgs, sizeof(inT32), 1, file); - bool swap = (num_dawgs > MAX_WERD_LENGTH); - if (swap) num_dawgs = reverse32(num_dawgs); - inT32 word_length; - int max_word_length = 0; - // Read and record pointers to fixed-length dawgs such that: - // dawg_vec[word_length] = pointer to dawg with word length of word_length, - // NULL if such fixed-length dawg does not exist. - for (i = 0; i < num_dawgs; ++i) { - fread(&word_length, sizeof(inT32), 1, file); - if (swap) word_length = reverse32(word_length); - ASSERT_HOST(word_length > 0 && word_length <= MAX_WERD_LENGTH); - while (word_length >= dawg_vec->size()) dawg_vec->push_back(NULL); - (*dawg_vec)[word_length] = - new SquishedDawg(file, type, lang, perm, debug_level); - if (word_length > max_word_length) max_word_length = word_length; - } - *max_wdlen = max_word_length; - // Entries dawg_vec[0] to dawg_vec[max_word_length] now hold pointers - // to fixed-length dawgs. The rest of the vector will contain the dawg - // pointers from the original input dawg_vec. - for (i = 0; i < dawg_vec_copy.size(); ++i) { - dawg_vec->push_back(dawg_vec_copy[i]); - } -} - -void Dict::WriteFixedLengthDawgs( - const GenericVector &dawg_vec, - int num_dawgs, int debug_level, FILE *output_file) { - fwrite(&num_dawgs, sizeof(inT32), 1, output_file); - if (debug_level) tprintf("Writing %d split length dawgs\n", num_dawgs); - for (int i = 1; i < dawg_vec.size(); ++i) { - if ((dawg_vec)[i] != NULL) { - fwrite(&i, sizeof(inT32), 1, output_file); - dawg_vec[i]->write_squished_dawg(output_file); - if (debug_level) tprintf("Wrote Dawg with word length %d\n", i); + if (dawg->permuter() > *curr_perm) *curr_perm = dawg->permuter(); + updated_dawgs->add_unique( + DawgPosition(pos.dawg_index, edge, pos.punc_index, pos.punc_ref, + pos.back_to_punc), + dawg_debug_level > 0, + "Append current dawg to updated active dawgs: "); } } } @@ -640,48 +504,45 @@ void Dict::WriteFixedLengthDawgs( // Fill the given active_dawgs vector with dawgs that could contain the // beginning of the word. If hyphenated() returns true, copy the entries // from hyphen_active_dawgs_ instead. -void Dict::init_active_dawgs(int sought_word_length, - DawgInfoVector *active_dawgs, +void Dict::init_active_dawgs(DawgPositionVector *active_dawgs, bool ambigs_mode) const { int i; - if (sought_word_length != kAnyWordLength) { - // Only search one fixed word length dawg. - if (sought_word_length <= max_fixed_length_dawgs_wdlen_ && - dawgs_[sought_word_length] != NULL) { - *active_dawgs += DawgInfo(sought_word_length, NO_EDGE); - } - } else if (hyphenated()) { + if (hyphenated()) { *active_dawgs = hyphen_active_dawgs_; if (dawg_debug_level >= 3) { for (i = 0; i < hyphen_active_dawgs_.size(); ++i) { tprintf("Adding hyphen beginning dawg [%d, " REFFORMAT "]\n", hyphen_active_dawgs_[i].dawg_index, - hyphen_active_dawgs_[i].ref); + hyphen_active_dawgs_[i].dawg_ref); } } } else { - for (i = 0; i < dawgs_.length(); ++i) { - if (dawgs_[i] != NULL && kBeginningDawgsType[(dawgs_[i])->type()] && - !(ambigs_mode && (dawgs_[i])->type() == DAWG_TYPE_PATTERN)) { - *active_dawgs += DawgInfo(i, NO_EDGE); - if (dawg_debug_level >= 3) { - tprintf("Adding beginning dawg [%d, " REFFORMAT "]\n", i, NO_EDGE); - } - } - } + default_dawgs(active_dawgs, ambigs_mode); } } -// If hyphenated() returns true, copy the entries from hyphen_constraints_ -// into the given constraints vector. -void Dict::init_constraints(DawgInfoVector *constraints) const { - if (hyphenated()) { - *constraints = hyphen_constraints_; - if (dawg_debug_level >= 3) { - for (int i = 0; i < hyphen_constraints_.size(); ++i) { - tprintf("Adding hyphen constraint [%d, " REFFORMAT "]\n", - hyphen_constraints_[i].dawg_index, - hyphen_constraints_[i].ref); +void Dict::default_dawgs(DawgPositionVector *dawg_pos_vec, + bool suppress_patterns) const { + bool punc_dawg_available = + (punc_dawg_ != NULL) && + punc_dawg_->edge_char_of(0, Dawg::kPatternUnicharID, true) != NO_EDGE; + + for (int i = 0; i < dawgs_.length(); i++) { + if (dawgs_[i] != NULL && + !(suppress_patterns && (dawgs_[i])->type() == DAWG_TYPE_PATTERN)) { + int dawg_ty = dawgs_[i]->type(); + bool subsumed_by_punc = kDawgSuccessors[DAWG_TYPE_PUNCTUATION][dawg_ty]; + if (dawg_ty == DAWG_TYPE_PUNCTUATION) { + *dawg_pos_vec += DawgPosition(-1, NO_EDGE, i, NO_EDGE, false); + if (dawg_debug_level >= 3) { + tprintf("Adding beginning punc dawg [%d, " REFFORMAT "]\n", i, + NO_EDGE); + } + } else if (!punc_dawg_available || !subsumed_by_punc) { + *dawg_pos_vec += DawgPosition(i, NO_EDGE, -1, NO_EDGE, false); + if (dawg_debug_level >= 3) { + tprintf("Adding beginning dawg [%d, " REFFORMAT "]\n", i, NO_EDGE); + } } } } @@ -700,8 +561,7 @@ void Dict::add_document_word(const WERD_CHOICE &best_choice) { FILE *doc_word_file; int stringlen = best_choice.length(); - if (!doc_dict_enable || valid_word(best_choice) || - CurrentWordAmbig() || stringlen < 2) + if (valid_word(best_choice) || stringlen < 2) return; // Discard words that contain >= kDocDictMaxRepChars repeating unichars. @@ -747,25 +607,48 @@ void Dict::add_document_word(const WERD_CHOICE &best_choice) { } void Dict::adjust_word(WERD_CHOICE *word, - float *certainty_array, - const BLOB_CHOICE_LIST_VECTOR *char_choices, bool nonword, + XHeightConsistencyEnum xheight_consistency, float additional_adjust, + bool modify_rating, bool debug) { - bool is_han = (char_choices != NULL && - getUnicharset().han_sid() != getUnicharset().null_sid() && - get_top_word_script(*char_choices, getUnicharset()) == - getUnicharset().han_sid()); + bool is_han = (getUnicharset().han_sid() != getUnicharset().null_sid() && + word->GetTopScriptID() == getUnicharset().han_sid()); bool case_is_ok = (is_han || case_ok(*word, getUnicharset())); bool punc_is_ok = (is_han || !nonword || valid_punctuation(*word)); float adjust_factor = additional_adjust; float new_rating = word->rating(); + new_rating += kRatingPad; + const char *xheight_triggered = ""; + if (word->length() > 1) { + // Calculate x-height and y-offset consistency penalties. + switch (xheight_consistency) { + case XH_INCONSISTENT: + adjust_factor += xheight_penalty_inconsistent; + xheight_triggered = ", xhtBAD"; + break; + case XH_SUBNORMAL: + adjust_factor += xheight_penalty_subscripts; + xheight_triggered = ", xhtSUB"; + break; + case XH_GOOD: + // leave the factor alone - all good! + break; + } + // TODO(eger): if nonword is true, but there is a "core" thats' a dict + // word, negate nonword status. + } else { + if (debug) { + tprintf("Consistency could not be calculated.\n"); + } + } if (debug) { - tprintf("%sWord: %s %4.2f ", nonword ? "Non-" : "", - word->debug_string().string(), word->rating()); + tprintf("%sWord: %s %4.2f%s", nonword ? "Non-" : "", + word->unichar_string().string(), word->rating(), + xheight_triggered); } - new_rating += kRatingPad; + if (nonword) { // non-dictionary word if (case_is_ok && punc_is_ok) { adjust_factor += segment_penalty_dict_nonword; @@ -798,10 +681,9 @@ void Dict::adjust_word(WERD_CHOICE *word, } } new_rating -= kRatingPad; - word->set_rating(new_rating); + if (modify_rating) word->set_rating(new_rating); if (debug) tprintf(" %4.2f --> %4.2f\n", adjust_factor, new_rating); - LogNewChoice(adjust_factor, certainty_array, false, word, - *char_choices); + word->set_adjust_factor(adjust_factor); } int Dict::valid_word(const WERD_CHOICE &word, bool numbers_ok) const { @@ -814,34 +696,25 @@ int Dict::valid_word(const WERD_CHOICE &word, bool numbers_ok) const { } if (word_ptr->length() == 0) return NO_PERM; // Allocate vectors for holding current and updated - // active_dawgs and constraints and initialize them. - DawgInfoVector *active_dawgs = new DawgInfoVector[2]; - DawgInfoVector *constraints = new DawgInfoVector[2]; - init_active_dawgs(kAnyWordLength, &(active_dawgs[0]), false); - init_constraints(&(constraints[0])); - DawgArgs dawg_args(&(active_dawgs[0]), &(constraints[0]), - &(active_dawgs[1]), &(constraints[1]), - 0.0, NO_PERM, kAnyWordLength, 0); + // active_dawgs and initialize them. + DawgPositionVector *active_dawgs = new DawgPositionVector[2]; + init_active_dawgs(&(active_dawgs[0]), false); + DawgArgs dawg_args(&(active_dawgs[0]), &(active_dawgs[1]), NO_PERM); int last_index = word_ptr->length() - 1; // Call leter_is_okay for each letter in the word. for (int i = hyphen_base_size(); i <= last_index; ++i) { if (!((this->*letter_is_okay_)(&dawg_args, word_ptr->unichar_id(i), i == last_index))) break; // Swap active_dawgs, constraints with the corresponding updated vector. - if (dawg_args.updated_active_dawgs == &(active_dawgs[1])) { - dawg_args.updated_active_dawgs = &(active_dawgs[0]); - dawg_args.updated_constraints = &(constraints[0]); + if (dawg_args.updated_dawgs == &(active_dawgs[1])) { + dawg_args.updated_dawgs = &(active_dawgs[0]); ++(dawg_args.active_dawgs); - ++(dawg_args.constraints); } else { - ++(dawg_args.updated_active_dawgs); - ++(dawg_args.updated_constraints); + ++(dawg_args.updated_dawgs); dawg_args.active_dawgs = &(active_dawgs[0]); - dawg_args.constraints = &(constraints[0]); } } delete[] active_dawgs; - delete[] constraints; return valid_word_permuter(dawg_args.permuter, numbers_ok) ? dawg_args.permuter : NO_PERM; } @@ -862,17 +735,30 @@ bool Dict::valid_bigram(const WERD_CHOICE &word1, if (w2start >= w2end) return word2.length() < 3; const UNICHARSET& uchset = getUnicharset(); - STRING bigram_string; + GenericVector bigram_string; + bigram_string.reserve(w1end + w2end + 1); for (int i = w1start; i < w1end; i++) { - UNICHAR_ID ch = NormalizeUnicharIdForMatch(word1.unichar_id(i)); - bigram_string += uchset.get_isdigit(ch) ? "?" : uchset.id_to_unichar(ch); - } - bigram_string += " "; + const GenericVector& normed_ids = + getUnicharset().normed_ids(word1.unichar_id(i)); + if (normed_ids.size() == 1 && uchset.get_isdigit(normed_ids[0])) + bigram_string.push_back(question_unichar_id_); + else + bigram_string += normed_ids; + } + bigram_string.push_back(UNICHAR_SPACE); for (int i = w2start; i < w2end; i++) { - UNICHAR_ID ch = NormalizeUnicharIdForMatch(word2.unichar_id(i)); - bigram_string += uchset.get_isdigit(ch) ? "?" : uchset.id_to_unichar(ch); + const GenericVector& normed_ids = + getUnicharset().normed_ids(word2.unichar_id(i)); + if (normed_ids.size() == 1 && uchset.get_isdigit(normed_ids[0])) + bigram_string.push_back(question_unichar_id_); + else + bigram_string += normed_ids; + } + WERD_CHOICE normalized_word(&uchset, bigram_string.size()); + for (int i = 0; i < bigram_string.size(); ++i) { + normalized_word.append_unichar_id_space_allocated(bigram_string[i], 1, + 0.0f, 0.0f); } - WERD_CHOICE normalized_word(bigram_string.string(), uchset); return bigram_dawg_->word_in_dawg(normalized_word); } @@ -902,39 +788,5 @@ bool Dict::valid_punctuation(const WERD_CHOICE &word) { return false; } -// Returns the "dominant" script ID for the word. By "dominant", the script -// must account for at least half the characters. Otherwise, it returns 0. -// Note that for Japanese, Hiragana and Katakana are simply treated as Han. -int Dict::get_top_word_script(const BLOB_CHOICE_LIST_VECTOR &char_choices, - const UNICHARSET &unicharset) { - int max_script = unicharset.get_script_table_size(); - int *sid = new int[max_script]; - int x; - for (x = 0; x < max_script; x++) sid[x] = 0; - for (x = 0; x < char_choices.length(); ++x) { - BLOB_CHOICE_IT blob_choice_it(char_choices.get(x)); - sid[blob_choice_it.data()->script_id()]++; - } - if (unicharset.han_sid() != unicharset.null_sid()) { - // Add the Hiragana & Katakana counts to Han and zero them out. - if (unicharset.hiragana_sid() != unicharset.null_sid()) { - sid[unicharset.han_sid()] += sid[unicharset.hiragana_sid()]; - sid[unicharset.hiragana_sid()] = 0; - } - if (unicharset.katakana_sid() != unicharset.null_sid()) { - sid[unicharset.han_sid()] += sid[unicharset.katakana_sid()]; - sid[unicharset.katakana_sid()] = 0; - } - } - // Note that high script ID overrides lower one on a tie, thus biasing - // towards non-Common script (if sorted that way in unicharset file). - int max_sid = 0; - for (x = 1; x < max_script; x++) - if (sid[x] >= sid[max_sid]) max_sid = x; - if (sid[max_sid] < char_choices.length() / 2) - max_sid = unicharset.null_sid(); - delete[] sid; - return max_sid; -} } // namespace tesseract diff --git a/dict/dict.h b/dict/dict.h index 78f8289282..213b2cab2e 100644 --- a/dict/dict.h +++ b/dict/dict.h @@ -21,6 +21,7 @@ #include "ambigs.h" #include "dawg.h" +#include "dawg_cache.h" #include "host.h" #include "image.h" #include "oldlist.h" @@ -28,7 +29,10 @@ #include "stopper.h" #include "trie.h" #include "unicharset.h" -#include "permute.h" +#include "params_training_featdef.h" + +class MATRIX; +class WERD_RES; #define MAX_WERD_LENGTH (inT64) 128 #define NO_RATING -1 @@ -49,11 +53,15 @@ typedef GenericVector DawgVector; // // Constants // -static const int kAnyWordLength = -1; static const int kRatingPad = 4; +static const char kDictWildcard[] = "\u2606"; // WHITE STAR +static const int kDictMaxWildcards = 2; // max wildcards for a word // TODO(daria): If hyphens are different in different languages and can be // inferred from training data we should load their values dynamically. static const char kHyphenSymbol[] = "-"; +static const char kSlashSymbol[] = "/"; +static const char kQuestionSymbol[] = "?"; +static const char kApostropheSymbol[] = "'"; static const int kMaxNumDawgEdgees = 2000000; static const int kMaxDocDawgEdges = 250000; static const int kMaxUserDawgEdges = 50000; @@ -62,29 +70,21 @@ static const float kSimCertaintyOffset = -10.0; // similarity matcher offset static const float kSimilarityFloor = 100.0; // worst E*L product to stop on static const int kDocDictMaxRepChars = 4; +// Enum for describing whether the x-height for the word is consistent: +// 0 - everything is good. +// 1 - there are one or two secondary (but consistent) baselines +// [think subscript and superscript], or there is an oversized +// first character. +// 2 - the word is inconsistent. +enum XHeightConsistencyEnum {XH_GOOD, XH_SUBNORMAL, XH_INCONSISTENT}; + struct DawgArgs { - DawgArgs(DawgInfoVector *d, DawgInfoVector *c, DawgInfoVector *ud, - DawgInfoVector *uc, float r, PermuterType p, int len, int e) : - active_dawgs(d), constraints(c), updated_active_dawgs(ud), - updated_constraints(uc), rating_margin(r) { - for (int i = 0; i < MAX_WERD_LENGTH; ++i) { - rating_array[i] = NO_RATING; - } - permuter = p; - sought_word_length = len; - end_char_choice_index = e; - } - DawgInfoVector *active_dawgs; - DawgInfoVector *constraints; - DawgInfoVector *updated_active_dawgs; - DawgInfoVector *updated_constraints; - PermuterType permuter; - int sought_word_length; + DawgArgs(DawgPositionVector *d, DawgPositionVector *up, PermuterType p) + : active_dawgs(d), updated_dawgs(up), permuter(p) {} - // TODO(daria): remove these fields when permdawg is deprecated. - float rating_margin; /**< pruning margin ratio */ - float rating_array[MAX_WERD_LENGTH]; - int end_char_choice_index; + DawgPositionVector *active_dawgs; + DawgPositionVector *updated_dawgs; + PermuterType permuter; }; class Dict { @@ -103,20 +103,31 @@ class Dict { UNICHARSET& getUnicharset() { return getImage()->getCCUtil()->unicharset; } - const UnicharAmbigs &getUnicharAmbigs() { + const UnicharAmbigs &getUnicharAmbigs() const { return getImage()->getCCUtil()->unichar_ambigs; } + // Returns true if unichar_id is a word compounding character like - or /. inline bool compound_marker(UNICHAR_ID unichar_id) { - return (unichar_id == getUnicharset().unichar_to_id("-") || - unichar_id == getUnicharset().unichar_to_id("/")); + const GenericVector& normed_ids = + getUnicharset().normed_ids(unichar_id); + return normed_ids.size() == 1 && + (normed_ids[0] == hyphen_unichar_id_ || + normed_ids[0] == slash_unichar_id_); + } + // Returns true if unichar_id is an apostrophe-like character that may + // separate prefix/suffix words from a main body word. + inline bool is_apostrophe(UNICHAR_ID unichar_id) { + const GenericVector& normed_ids = + getUnicharset().normed_ids(unichar_id); + return normed_ids.size() == 1 && normed_ids[0] == apostrophe_unichar_id_; } /* hyphen.cpp ************************************************************/ /// Returns true if we've recorded the beginning of a hyphenated word. inline bool hyphenated() const { return - !last_word_on_line_ && hyphen_word_ && GetMaxFixedLengthDawgIndex() < 0; + !last_word_on_line_ && hyphen_word_; } /// Size of the base word (the part on the line before) of a hyphenated word. inline int hyphen_base_size() const { @@ -131,19 +142,13 @@ class Dict { if (hyphen_debug_level) word->print("copy_hyphen_info: "); } } - /// Erase the unichar ids corresponding to the portion of the word - /// from the previous line. The word is not changed if it is not - /// split between lines and hyphenated. - inline void remove_hyphen_head(WERD_CHOICE *word) const { - if (this->hyphenated()) { - word->remove_unichar_ids(0, hyphen_word_->length()); - if (hyphen_debug_level) hyphen_word_->print("remove_hyphen_head: "); - } - } /// Check whether the word has a hyphen at the end. inline bool has_hyphen_end(UNICHAR_ID unichar_id, bool first_pos) const { - return (last_word_on_line_ && !first_pos && - unichar_id == hyphen_unichar_id_); + if (!last_word_on_line_ || first_pos) + return false; + const GenericVector& normed_ids = + getUnicharset().normed_ids(unichar_id); + return normed_ids.size() == 1 && normed_ids[0] == hyphen_unichar_id_; } /// Same as above, but check the unichar at the end of the word. inline bool has_hyphen_end(const WERD_CHOICE &word) const { @@ -152,54 +157,45 @@ class Dict { } /// Unless the previous word was the last one on the line, and the current /// one is not (thus it is the first one on the line), erase hyphen_word_, - /// clear hyphen_active_dawgs_, hyphen_constraints_ update last_word_on_line_. + /// clear hyphen_active_dawgs_, update last_word_on_line_. void reset_hyphen_vars(bool last_word_on_line); - /// Update hyphen_word_, and copy the given DawgInfoVectors into - /// hyphen_active_dawgs_ and hyphen_constraints_. + /// Update hyphen_word_, and copy the given DawgPositionVectors into + /// hyphen_active_dawgs_ . void set_hyphen_word(const WERD_CHOICE &word, - const DawgInfoVector &active_dawgs, - const DawgInfoVector &constraints); + const DawgPositionVector &active_dawgs); /* permdawg.cpp ************************************************************/ + // Note: Functions in permdawg.cpp are only used by NoDangerousAmbig(). + // When this function is refactored, permdawg.cpp can be removed. + /// Copies word into best_choice if its rating is smaller /// than that of best_choice. inline void update_best_choice(const WERD_CHOICE &word, WERD_CHOICE *best_choice) { - if (word.rating() < best_choice->rating()) *best_choice = word; + if (word.rating() < best_choice->rating()) { + *best_choice = word; + } } /// Fill the given active_dawgs vector with dawgs that could contain the /// beginning of the word. If hyphenated() returns true, copy the entries /// from hyphen_active_dawgs_ instead. - void init_active_dawgs(int sought_word_length, - DawgInfoVector *active_dawgs, + void init_active_dawgs(DawgPositionVector *active_dawgs, bool ambigs_mode) const; - /// If hyphenated() returns true, copy the entries from hyphen_constraints_ - /// into the given constraints vector. - void init_constraints(DawgInfoVector *constraints) const; - /// Returns true if we are operating in ambigs mode. - inline bool ambigs_mode(float rating_limit) { - return rating_limit <= 0.0; - } + // Fill the given vector with the default collection of any-length dawgs + void default_dawgs(DawgPositionVector *anylength_dawgs, + bool suppress_patterns) const; + + /// Recursively explore all the possible character combinations in /// the given char_choices. Use go_deeper_dawg_fxn() to explore all the /// dawgs in the dawgs_ vector in parallel and discard invalid words. /// /// Allocate and return a WERD_CHOICE with the best valid word found. WERD_CHOICE *dawg_permute_and_select( - const BLOB_CHOICE_LIST_VECTOR &char_choices, float rating_limit, - int sought_word_length, int end_char_choice_index); - WERD_CHOICE *dawg_permute_and_select( - const BLOB_CHOICE_LIST_VECTOR &char_choices, float rating_limit) { - return dawg_permute_and_select(char_choices, rating_limit, - kAnyWordLength, 0); - } + const BLOB_CHOICE_LIST_VECTOR &char_choices, float rating_limit); /// If the choice being composed so far could be a dictionary word /// and we have not reached the end of the word keep exploring the /// char_choices further. - /// Also: - /// -- sets hyphen word if needed - /// -- if word_ending is true and the word is better than best_choice, - /// copies word to best_choice and logs new word choice void go_deeper_dawg_fxn( const char *debug, const BLOB_CHOICE_LIST_VECTOR &char_choices, int char_choice_index, const CHAR_FRAGMENT_INFO *prev_char_frag_info, @@ -207,79 +203,18 @@ class Dict { float *limit, WERD_CHOICE *best_choice, int *attempts_left, void *void_more_args); - /* permute.cpp *************************************************************/ - WERD_CHOICE *get_top_choice_word( - const BLOB_CHOICE_LIST_VECTOR &char_choices); - WERD_CHOICE *permute_top_choice( - const BLOB_CHOICE_LIST_VECTOR &char_choices, - float* rating_limit, - WERD_CHOICE *raw_choice, - BOOL8 *any_alpha); - const char* choose_il1(const char *first_char, //first choice - const char *second_char, //second choice - const char *third_char, //third choice - const char *prev_char, //prev in word - const char *next_char, //next in word - const char *next_next_char); //after next next in word - WERD_CHOICE *permute_all(const BLOB_CHOICE_LIST_VECTOR &char_choices, - const WERD_CHOICE *best_choice, - WERD_CHOICE *raw_choice); - void end_permute(); - void permute_subword(const BLOB_CHOICE_LIST_VECTOR &char_choices, - float rating_limit, - int start, - int end, - WERD_CHOICE *current_word); - bool permute_characters(const BLOB_CHOICE_LIST_VECTOR &char_choices, - WERD_CHOICE *best_choice, - WERD_CHOICE *raw_choice); - WERD_CHOICE *permute_compound_words( - const BLOB_CHOICE_LIST_VECTOR &char_choices, - float rating_limit); - /// Find permutations matching a list of fixed-char-length dawgs - /// The bestchoice based on this permuter alone is returned. Alternatively, - /// non-conflicting changes can be combined through permuter_state. - WERD_CHOICE *permute_fixed_length_words( - const BLOB_CHOICE_LIST_VECTOR &char_choices, - PermuterState *permuter_state); - /// Incoporate segmentation cost into word rating - void incorporate_segcost(WERD_CHOICE* word); - /// Checks for script-consistent permutations. Similar to fixed-length - /// permuter, the best choice is returned by the function, but the combined - /// changes are also recorded into permuter_state. - WERD_CHOICE *permute_script_words( - const BLOB_CHOICE_LIST_VECTOR &char_choices, - PermuterState *permuter_state); - /// checks for consistency in character property (eg. alpah, digit, punct) - WERD_CHOICE *permute_chartype_words( - const BLOB_CHOICE_LIST_VECTOR &char_choices, - PermuterState *permuter_state); - - /// Look up the main chartype for each character position and store it in - /// the given array. Also returns the dominant type from unambiguous top - /// choices. - char top_word_chartype(const BLOB_CHOICE_LIST_VECTOR &char_choices, - char* pos_chartypes); - - WERD_CHOICE *top_fragments_permute_and_select( - const BLOB_CHOICE_LIST_VECTOR &char_choices, - float rating_limit); - /// While the choice being composed so far could be better - /// than best_choice keeps exploring char_choices. - /// If the end of the word is reached and the word is better than - /// best_choice, copies word to best_choice and logs the new word choice. - void go_deeper_top_fragments_fxn( - const char *debug, const BLOB_CHOICE_LIST_VECTOR &char_choices, - int char_choice_index, const CHAR_FRAGMENT_INFO *prev_char_frag_info, - bool word_ending, WERD_CHOICE *word, float certainties[], float *limit, - WERD_CHOICE *best_choice, int *attempts_left, void *more_args); - - /// Semi-generic functions used by multiple permuters. - bool fragment_state_okay(UNICHAR_ID curr_unichar_id, - float curr_rating, float curr_certainty, - const CHAR_FRAGMENT_INFO *prev_char_frag_info, - const char *debug, int word_ending, - CHAR_FRAGMENT_INFO *char_frag_info); + /// Pointer to go_deeper function. + void (Dict::*go_deeper_fxn_)(const char *debug, + const BLOB_CHOICE_LIST_VECTOR &char_choices, + int char_choice_index, + const CHAR_FRAGMENT_INFO *prev_char_frag_info, + bool word_ending, WERD_CHOICE *word, + float certainties[], float *limit, + WERD_CHOICE *best_choice, int *attempts_left, + void *void_more_args); + // + // Helper functions for dawg_permute_and_select(). + // void permute_choices( const char *debug, const BLOB_CHOICE_LIST_VECTOR &char_choices, @@ -304,68 +239,31 @@ class Dict { WERD_CHOICE *best_choice, int *attempts_left, void *more_args); - /// Pointer to go_deeper function that will be modified by various permuters. - void (Dict::*go_deeper_fxn_)(const char *debug, - const BLOB_CHOICE_LIST_VECTOR &char_choices, - int char_choice_index, - const CHAR_FRAGMENT_INFO *prev_char_frag_info, - bool word_ending, WERD_CHOICE *word, - float certainties[], float *limit, - WERD_CHOICE *best_choice, int *attempts_left, - void *void_more_args); + + bool fragment_state_okay(UNICHAR_ID curr_unichar_id, + float curr_rating, float curr_certainty, + const CHAR_FRAGMENT_INFO *prev_char_frag_info, + const char *debug, int word_ending, + CHAR_FRAGMENT_INFO *char_frag_info); + /* stopper.cpp *************************************************************/ bool NoDangerousAmbig(WERD_CHOICE *BestChoice, DANGERR *fixpt, bool fix_replaceable, - BLOB_CHOICE_LIST_VECTOR *Choices, - bool *modified_blobs); - double StopperAmbigThreshold(double f1, double f2) { - return (f2 - f1) * stopper_ambiguity_threshold_gain - - stopper_ambiguity_threshold_offset; - } - // If the certainty of any chunk in Choice (item1) is not ambiguous with the - // corresponding chunk in the best choice (item2), frees Choice and - // returns true. - int FreeBadChoice(void *item1, // VIABLE_CHOICE Choice - void *item2); // EXPANDED_CHOICE *BestChoice - /// Replaces the corresponding wrong ngram in werd_choice with the correct - /// one. We indicate that this newly inserted ngram unichar is composed from - /// several fragments and modify the corresponding entries in blob_choices to - /// contain fragments of the correct ngram unichar instead of the original - /// unichars. Ratings and certainties of entries in blob_choices and - /// werd_choice are unichaged. E.g. for werd_choice mystring'' and ambiguity - /// ''->": werd_choice becomes mystring", first ' in blob_choices becomes - /// |"|0|2, second one is set to |"|1|2. + MATRIX* ratings); + // Replaces the corresponding wrong ngram in werd_choice with the correct + // one. The whole correct n-gram is inserted into the ratings matrix and + // the werd_choice: no more fragments!. Rating and certainty of new entries + // in matrix and werd_choice are the sum and mean of the wrong ngram + // respectively. + // E.g. for werd_choice mystring'' and ambiguity ''->": werd_choice becomes + // mystring", with a new entry in the ratings matrix for ". void ReplaceAmbig(int wrong_ngram_begin_index, int wrong_ngram_size, UNICHAR_ID correct_ngram_id, WERD_CHOICE *werd_choice, - BLOB_CHOICE_LIST_VECTOR *blob_choices, - bool *modified_blobs); - - inline void DisableChoiceAccum() { keep_word_choices_ = false; } - inline void EnableChoiceAccum() { keep_word_choices_ = true; } - inline bool ChoiceAccumEnabled() { return keep_word_choices_; } + MATRIX *ratings); /// Returns the length of the shortest alpha run in WordChoice. int LengthOfShortestAlphaRun(const WERD_CHOICE &WordChoice); - /// Allocates a new viable choice data structure, copies WordChoice, - /// Certainties, and current_segmentation_ into it, returns a pointer to - /// the newly created VIABLE_CHOICE. - /// WordChoice is a choice to be converted to a viable choice. - /// AdjustFactor is a factor used to adjust ratings for WordChoice. - /// Certainties contain certainty for each character in WordChoice. - VIABLE_CHOICE NewViableChoice(const WERD_CHOICE &WordChoice, - FLOAT32 AdjustFactor, - const float Certainties[]); - /// Dumps a text representation of the specified Choice to File. - void PrintViableChoice(FILE *File, const char *Label, VIABLE_CHOICE Choice); - /// Compares unichar ids in word_choice to those in viable_choice, - /// returns true if they are the same. - bool StringSameAs(const WERD_CHOICE &WordChoice, - VIABLE_CHOICE ViableChoice); - /// Compares String to ViableChoice and returns true if they are the same. - bool StringSameAs(const char *String, - const char *String_lengths, - VIABLE_CHOICE ViableChoice); /// Returns true if the certainty of the BestChoice word is within a /// reasonable range of the average certainties for the best choices for /// each character in the segmentation. This test is used to catch words @@ -373,89 +271,17 @@ class Dict { /// word (i.e. false will be returned in that case). The algorithm computes /// the mean and std deviation of the certainties in the word with the worst /// certainty thrown out. - int UniformCertainties(const BLOB_CHOICE_LIST_VECTOR &Choices, - const WERD_CHOICE &BestChoice); + int UniformCertainties(const WERD_CHOICE& word); /// Returns true if the given best_choice is good enough to stop. - bool AcceptableChoice(BLOB_CHOICE_LIST_VECTOR *Choices, - WERD_CHOICE *BestChoice, - DANGERR *fixpt, - ACCEPTABLE_CHOICE_CALLER caller, - bool *modified_blobs); + bool AcceptableChoice(const WERD_CHOICE& best_choice, + XHeightConsistencyEnum xheight_consistency); /// Returns false if the best choice for the current word is questionable /// and should be tried again on the second pass or should be flagged to /// the user. - bool AcceptableResult(const WERD_CHOICE &BestChoice); - /// Compares the corresponding strings of WordChoice and ViableChoice and - /// returns true if they are the same. - int ChoiceSameAs(const WERD_CHOICE &WordChoice, VIABLE_CHOICE ViableChoice); - /// Adds Choice to ChoicesList if the adjusted certainty for Choice is within - /// a reasonable range of the best choice in ChoicesList. The ChoicesList list - /// is kept in sorted order by rating. Duplicates are removed. - /// WordChoice is the new choice for current word. - /// AdjustFactor is an adjustment factor which was applied to choice. - /// Certainties are certainties for each char in new choice. - /// raw_choice indicates whether WordChoice is a raw or best choice. - void LogNewChoice(FLOAT32 AdjustFactor, - const float Certainties[], - bool raw_choice, - WERD_CHOICE *WordChoice, - const BLOB_CHOICE_LIST_VECTOR &blob_choices); + bool AcceptableResult(WERD_RES* word); void EndDangerousAmbigs(); - /// Returns true if WordChoice is the same as the current best choice. - bool CurrentBestChoiceIs(const WERD_CHOICE &WordChoice); - /// Returns the adjustment factor for the best choice for the current word. - FLOAT32 CurrentBestChoiceAdjustFactor(); - /// Returns true if there are multiple good choices for the current word. - bool CurrentWordAmbig(); /// Prints the current choices for this word to stdout. void DebugWordChoices(); - /// Print all the choices in raw_choices_ list for non 1-1 ambiguities. - void PrintAmbigAlternatives(FILE *file, const char *label, - int label_num_unichars); - /// Fill ViableChoice with information from WordChoice, AChoice, AdjustFactor, - /// and Certainties. - void FillViableChoice(const WERD_CHOICE &WordChoice, - FLOAT32 AdjustFactor, const float Certainties[], - VIABLE_CHOICE ViableChoice); - /// Returns true if there are no alternative choices for the current word - /// or if all alternatives have an adjust factor worse than Threshold. - bool AlternativeChoicesWorseThan(FLOAT32 Threshold); - /// Removes from best_choices_ all choices which are not within a reasonable - /// range of the best choice. - void FilterWordChoices(); - /// Compares the best choice for the current word to the best raw choice - /// to determine which characters were classified incorrectly by the - /// classifier. Then places a separate threshold into Thresholds for each - /// character in the word. If the classifier was correct, MaxRating is placed - /// into Thresholds. If the classifier was incorrect, the avg. match rating - /// (error percentage) of the classifier's incorrect choice minus some margin - /// is placed into thresholds.This can then be used by the caller to try to - /// create a new template for the desired class that will classify the - /// character with a rating better than the threshold value. The match rating - /// placed into Thresholds is never allowed to be below MinRating in order to - /// prevent trying to make overly tight templates. - /// MinRating limits how tight to make a template. - /// MaxRating limits how loose to make a template. - /// RatingMargin denotes the amount of margin to put in template. - void FindClassifierErrors(FLOAT32 MinRating, - FLOAT32 MaxRating, - FLOAT32 RatingMargin, - FLOAT32 Thresholds[]); - /// Initializes the data structures used to keep track the good word choices - /// found for a word. - void InitChoiceAccum(); - /// Clears best_choices_ list accumulated by the stopper. - void ClearBestChoiceAccum(); - /// Updates the blob widths in current_segmentation_ to be the same as - /// provided in BlobWidth. BlobWidth[] contains the number of chunks in each - /// blob in the current segmentation. - void LogNewSegmentation(PIECES_STATE BlobWidth); - /// Given Blob (the index of the blob that was split), adds 1 chunk to the - /// specified blob for each choice in best_choices_ and for best_raw_choice_. - void LogNewSplit(int Blob); - /// Increments the chunk count of the character in Choice which corresponds - /// to Blob (index of the blob being split). - void AddNewChunk(VIABLE_CHOICE Choice, int Blob); /// Sets up stopper variables in preparation for the first pass. void SettupStopperPass1(); /// Sets up stopper variables in preparation for the second pass. @@ -471,7 +297,8 @@ class Dict { /// Initialize Dict class - load dawgs from [lang].traineddata and /// user-specified wordlist and parttern list. - void Load(); + static DawgCache *GlobalDawgCache(); + void Load(DawgCache *dawg_cache); void End(); // Resets the document dictionary analogous to ResetAdaptiveClassifier. @@ -482,15 +309,6 @@ class Dict { document_words_->clear(); } - // Create unicharset adaptations of known, short lists of UTF-8 equivalent - // characters (think all hyphen-like symbols). The first version of the - // list is taken as equivalent for matching against the dictionary. - void LoadEquivalenceList(const char *unichar_strings[]); - - // Normalize all hyphen and apostrophes to the canonicalized one for - // matching; pass everything else through as is. See LoadEquivalenceList(). - UNICHAR_ID NormalizeUnicharIdForMatch(UNICHAR_ID unichar_id) const; - /** * Returns the maximal permuter code (from ccstruct/ratngs.h) if in light * of the current state the letter at word_index in the given word @@ -498,59 +316,32 @@ class Dict { * otherwise returns NO_PERM. * * The state is described by void_dawg_args, which are interpreted as - * DawgArgs and contain two relevant input vectors: active_dawgs and - * constraints. Each entry in the active_dawgs vector contains an index + * DawgArgs and contain relevant active dawg positions. + * Each entry in the active_dawgs vector contains an index * into the dawgs_ vector and an EDGE_REF that indicates the last edge - * followed in the dawg. Each entry in the constraints vector contains - * an index into the dawgs_ vector and an EDGE_REF that indicates an edge - * in a pattern dawg followed to match a pattern. Currently constraints - * are used to save the state of punctuation dawgs after leading - * punctuation was found. + * followed in the dawg. It also may contain a position in the punctuation + * dawg which describes surrounding punctuation (see struct DawgPosition). * * Input: * At word_index 0 dawg_args->active_dawgs should contain an entry for each - * dawg whose type has a bit set in kBeginningDawgsType, - * dawg_args->constraints should be empty. EDGE_REFs in active_dawgs and - * constraints vectors should be initialized to NO_EDGE. If hyphen state - * needs to be applied, initial dawg_args->active_dawgs and - * dawg_args->constrains can be copied from the saved hyphen state - * (maintained by Dict). - * For word_index > 0 the corresponding state (active_dawgs and constraints) - * can be obtained from dawg_args->updated_* passed to def_letter_is_okay - * for word_index-1. - * Note: the function assumes that active_dags, constraints and updated_* + * dawg that may start at the beginning of a word, with punc_ref and edge_ref + * initialized to NO_EDGE. Since the punctuation dawg includes the empty + * pattern " " (meaning anything without surrounding punctuation), having a + * single entry for the punctuation dawg will cover all dawgs reachable + * therefrom -- that includes all number and word dawgs. The only dawg + * non-reachable from the punctuation_dawg is the pattern dawg. + * If hyphen state needs to be applied, initial dawg_args->active_dawgs can + * be copied from the saved hyphen state (maintained by Dict). + * For word_index > 0 the corresponding state (active_dawgs and punc position) + * can be obtained from dawg_args->updated_dawgs passed to + * def_letter_is_okay for word_index-1. + * Note: the function assumes that active_dawgs, nd updated_dawgs * member variables of dawg_args are not NULL. * * Output: - * The function fills in dawg_args->updated_active_dawgs vector with the + * The function fills in dawg_args->updated_dawgs vector with the * entries for dawgs that contain the word up to the letter at word_index. - * The new constraints (if any) are added to dawg_args->updated_constraints, - * the constraints from dawg_args->constraints are also copied into it. - * - * Detailed description: - * In order to determine whether the word is still valid after considering - * all the letters up to the one at word_index the following is done for - * each entry in dawg_args->active_dawgs: * - * - next starting node is obtained from entry.ref and edge_char_of() is - * called to obtain the next edge - * - if a valid edge is found, the function returns the updated permuter - * code true and an entry [entry.dawg_index, edge] is inserted in - * dawg_args->updated_active_dawgs - * otherwise: - * - if we are dealing with dawg of type DAWG_TYPE_PUNCTUATION, - * edge_char_of() is called again, but now with kPatternUnicharID - * as unichar_id; if a valid edge is found it is recorded in - * dawg_args->updated_constraints - * - the function checks whether the word can end with the previous - * letter - * - each successor of the dawg (e.g. dawgs with type DAWG_TYPE_WORD - * could be successors to dawgs with type DAWG_TYPE_PUNCTUATION; the - * successors are defined by successors_ vector) is explored and if - * a letter is found in the successor dawg, a new entry is inserted - * into dawg_args->updated_active_dawgs with EDGE_REF being either - * NO_EDGE or an EDGE_REF recorded in constraints vector for the - * corresponding dawg index */ // @@ -599,6 +390,20 @@ class Dict { const char* character, int character_bytes); + // Interface with params model. + float (Dict::*params_model_classify_)(const char *lang, void *path); + float ParamsModelClassify(const char *lang, void *path); + // Call params_model_classify_ member function. + float CallParamsModelClassify(void *path) { + ASSERT_HOST(params_model_classify_ != NULL); // ASSERT_HOST -> assert + return (this->*params_model_classify_)( + getImage()->getCCUtil()->lang.string(), path); + } + + inline void SetWildcardID(UNICHAR_ID id) { wildcard_unichar_id_ = id; } + inline const UNICHAR_ID WildcardID() const { + return wildcard_unichar_id_; + } /// Return the number of dawgs in the dawgs_ vector. inline const int NumDawgs() const { return dawgs_.size(); } /// Return i-th dawg pointer recorded in the dawgs_ vector. @@ -607,15 +412,6 @@ class Dict { inline const Dawg *GetPuncDawg() const { return punc_dawg_; } /// Return the points to the unambiguous words dawg. inline const Dawg *GetUnambigDawg() const { return unambig_dawg_; } - /// Return the pointer to the Dawg that contains words of length word_length. - inline const Dawg *GetFixedLengthDawg(int word_length) const { - if (word_length > max_fixed_length_dawgs_wdlen_) return NULL; - assert(dawgs_.size() > word_length); - return dawgs_[word_length]; - } - inline const int GetMaxFixedLengthDawgIndex() const { - return max_fixed_length_dawgs_wdlen_; - } /// Returns the appropriate next node given the EDGE_REF. static inline NODE_REF GetStartingNode(const Dawg *dawg, EDGE_REF edge_ref) { if (edge_ref == NO_EDGE) return 0; // beginning to explore the dawg @@ -623,27 +419,18 @@ class Dict { if (node == 0) node = NO_EDGE; // end of word return node; } - /// At word ending make sure all the recorded constraints are satisfied. - /// Each constraint signifies that we found a beginning pattern in a - /// pattern dawg. Check that this pattern can end here (e.g. if some - /// leading punctuation is found this would ensure that we are not - /// expecting any particular trailing punctuation after the word). - inline bool ConstraintsOk(const DawgInfoVector &constraints, - int word_end, DawgType current_dawg_type) const { - if (!word_end) return true; - if (current_dawg_type == DAWG_TYPE_PUNCTUATION) return true; - for (int c = 0; c < constraints.length(); ++c) { - const DawgInfo &cinfo = constraints[c]; - Dawg *cdawg = dawgs_[cinfo.dawg_index]; - if (!cdawg->end_of_word(cinfo.ref)) { - if (dawg_debug_level >= 3) { - tprintf("Constraint [%d, " REFFORMAT "] is not satisfied\n", - cinfo.dawg_index, cinfo.ref); - } - return false; - } + + // Given a unichar from a string and a given dawg, return the unichar + // we should use to match in that dawg type. (for example, in the number + // dawg, all numbers are transformed to kPatternUnicharId). + inline UNICHAR_ID char_for_dawg(UNICHAR_ID ch, const Dawg *dawg) const { + if (!dawg) return ch; + switch (dawg->type()) { + case DAWG_TYPE_NUMBER: + return getUnicharset().get_isdigit(ch) ? Dawg::kPatternUnicharID : ch; + default: + return ch; } - return true; } /// For each of the character classes of the given unichar_id (and the @@ -651,35 +438,21 @@ class Dict { /// in the given dawg and (after checking that it is valid) records it in /// dawg_args->updated_ative_dawgs. Updates current_permuter if any valid /// edges were found. - void ProcessPatternEdges(const Dawg *dawg, const DawgInfo &info, + void ProcessPatternEdges(const Dawg *dawg, const DawgPosition &info, UNICHAR_ID unichar_id, bool word_end, - DawgArgs *dawg_args, + DawgPositionVector *updated_dawgs, PermuterType *current_permuter) const; /// Read/Write/Access special purpose dawgs which contain words /// only of a certain length (used for phrase search for /// non-space-delimited languages). - /// Reads a sequence of dawgs from the given file. - /// Appends the constructed dawgs to the given dawg_vec. - /// Fills the given table with indices of the dawgs in the - /// dawg_vec corresponding to the dawgs with words - /// of a particular length. - static void ReadFixedLengthDawgs(DawgType type, const STRING &lang, - PermuterType perm, int debug_level, - FILE *file, DawgVector *dawg_vec, - int *max_wdlen); - /// Writes the dawgs in the dawgs_vec to a file. Updates the given table with - /// the indices of dawgs in the dawg_vec for the corresponding word lengths. - static void WriteFixedLengthDawgs( - const GenericVector &dawg_vec, - int num_dawgs, int debug_level, FILE *output_file); - /// Check all the DAWGs to see if this word is in any of them. inline static bool valid_word_permuter(uinT8 perm, bool numbers_ok) { return (perm == SYSTEM_DAWG_PERM || perm == FREQ_DAWG_PERM || perm == DOC_DAWG_PERM || perm == USER_DAWG_PERM || - perm == USER_PATTERN_PERM || (numbers_ok && perm == NUMBER_PERM)); + perm == USER_PATTERN_PERM || perm == COMPOUND_PERM || + (numbers_ok && perm == NUMBER_PERM)); } int valid_word(const WERD_CHOICE &word, bool numbers_ok) const; int valid_word(const WERD_CHOICE &word) const { @@ -704,30 +477,16 @@ class Dict { int good_choice(const WERD_CHOICE &choice); /// Adds a word found on this document to the document specific dictionary. void add_document_word(const WERD_CHOICE &best_choice); - int get_top_word_script(const BLOB_CHOICE_LIST_VECTOR &char_choices, - const UNICHARSET &unicharset); /// Adjusts the rating of the given word. - void adjust_word(WERD_CHOICE *word, float *certainty_array, - const BLOB_CHOICE_LIST_VECTOR *char_choices, - bool nonword, float additional_adjust, bool debug); - void adjust_word(WERD_CHOICE *word, float *certainty_array, - const BLOB_CHOICE_LIST_VECTOR *char_choices, - bool debug) { - adjust_word(word, certainty_array, char_choices, false, 0.0f, - debug); - } - void adjust_non_word(WERD_CHOICE *word, - float *certainty_array, - const BLOB_CHOICE_LIST_VECTOR *char_choices, - bool debug) { - adjust_word(word, certainty_array, char_choices, true, 0.0f, debug); - } + void adjust_word(WERD_CHOICE *word, + bool nonword, XHeightConsistencyEnum xheight_consistency, + float additional_adjust, + bool modify_rating, + bool debug); /// Set wordseg_rating_adjust_factor_ to the given value. inline void SetWordsegRatingAdjustFactor(float f) { wordseg_rating_adjust_factor_ = f; } - // Accessor for best_choices_. - const LIST &getBestChoices() { return best_choices_; } private: /** Private member variables. */ @@ -741,30 +500,26 @@ class Dict { UnicharAmbigs *dang_ambigs_table_; /** Same as above, but for ambiguities with replace flag set. */ UnicharAmbigs *replace_ambigs_table_; - /** - * Flag used to disable accumulation of word choices - * during compound word permutation. - */ - bool keep_word_choices_; /** Additional certainty padding allowed before a word is rejected. */ FLOAT32 reject_offset_; - /** Current word segmentation. */ - PIECES_STATE current_segmentation_; - /** Variables to keep track of best/raw word choices. */ - VIABLE_CHOICE best_raw_choice_; - LIST raw_choices_; - LIST best_choices_; + // Cached UNICHAR_IDs: + UNICHAR_ID wildcard_unichar_id_; // kDictWildcard. + UNICHAR_ID apostrophe_unichar_id_; // kApostropheSymbol. + UNICHAR_ID question_unichar_id_; // kQuestionSymbol. + UNICHAR_ID slash_unichar_id_; // kSlashSymbol. + UNICHAR_ID hyphen_unichar_id_; // kHyphenSymbol. // Hyphen-related variables. - UNICHAR_ID hyphen_unichar_id_; WERD_CHOICE *hyphen_word_; - DawgInfoVector hyphen_active_dawgs_; - DawgInfoVector hyphen_constraints_; + DawgPositionVector hyphen_active_dawgs_; bool last_word_on_line_; // List of lists of "equivalent" UNICHAR_IDs for the purposes of dictionary // matching. The first member of each list is taken as canonical. For // example, the first list contains hyphens and dashes with the first symbol // being the ASCII hyphen minus. GenericVector > equivalent_symbols_; + // Dawg Cache reference - this is who we ask to allocate/deallocate dawgs. + DawgCache *dawg_cache_; + bool dawg_cache_is_ours_; // we should delete our own dawg_cache_ // Dawgs. DawgVector dawgs_; SuccessorListsVector successors_; @@ -783,9 +538,6 @@ class Dict { Dawg *unambig_dawg_; Dawg *punc_dawg_; Trie *document_words_; - /// Maximum word length of fixed-length word dawgs. - /// A value < 1 indicates that no fixed-length dawgs are loaded. - int max_fixed_length_dawgs_wdlen_; /// Current segmentation cost adjust factor for word rating. /// See comments in incorporate_segcost. float wordseg_rating_adjust_factor_; @@ -805,10 +557,14 @@ class Dict { BOOL_VAR_H(load_punc_dawg, true, "Load dawg with punctuation patterns."); BOOL_VAR_H(load_number_dawg, true, "Load dawg with number patterns."); - BOOL_VAR_H(load_fixed_length_dawgs, true, "Load fixed length" - " dawgs (e.g. for non-space delimited languages)"); - BOOL_VAR_H(load_bigram_dawg, false, + BOOL_VAR_H(load_bigram_dawg, true, "Load dawg with special word bigrams."); + double_VAR_H(xheight_penalty_subscripts, 0.125, + "Score penalty (0.1 = 10%) added if there are subscripts " + "or superscripts in a word, but it is otherwise OK."); + double_VAR_H(xheight_penalty_inconsistent, 0.25, + "Score penalty (0.1 = 10%) added if an xheight is " + "inconsistent."); double_VAR_H(segment_penalty_dict_frequent_word, 1.0, "Score multiplier for word matches which have good case and" "are frequent in the given language (lower is better)."); @@ -857,57 +613,26 @@ class Dict { BOOL_VAR_H(stopper_no_acceptable_choices, false, "Make AcceptableChoice() always return false. Useful" " when there is a need to explore all segmentations"); - double_VAR_H(stopper_ambiguity_threshold_gain, 8.0, - "Gain factor for ambiguity threshold."); - double_VAR_H(stopper_ambiguity_threshold_offset, 1.5, - "Certainty offset for ambiguity threshold."); - BOOL_VAR_H(save_raw_choices, false, "Save all explored raw choices"); INT_VAR_H(tessedit_truncate_wordchoice_log, 10, "Max words to keep in list"); STRING_VAR_H(word_to_debug, "", "Word for which stopper debug information" " should be printed to stdout"); STRING_VAR_H(word_to_debug_lengths, "", "Lengths of unichars in word_to_debug"); INT_VAR_H(fragments_debug, 0, "Debug character fragments"); - INT_VAR_H(segment_debug, 0, "Debug the whole segmentation process"); - BOOL_VAR_H(permute_debug, 0, "Debug char permutation process"); - double_VAR_H(bestrate_pruning_factor, 2.0, "Multiplying factor of" - " current best rate to prune other hypotheses"); - BOOL_VAR_H(permute_script_word, 0, - "Turn on word script consistency permuter"); - BOOL_VAR_H(segment_segcost_rating, 0, - "incorporate segmentation cost in word rating?"); BOOL_VAR_H(segment_nonalphabetic_script, false, "Don't use any alphabetic-specific tricks." "Set to true in the traineddata config file for" " scripts that are cursive or inherently fixed-pitch"); - double_VAR_H(segment_reward_script, 0.95, - "Score multipler for script consistency within a word. " - "Being a 'reward' factor, it should be <= 1. " - "Smaller value implies bigger reward."); - BOOL_VAR_H(permute_fixed_length_dawg, 0, - "Turn on fixed-length phrasebook search permuter"); - BOOL_VAR_H(permute_chartype_word, 0, - "Turn on character type (property) consistency permuter"); - double_VAR_H(segment_reward_chartype, 0.97, - "Score multipler for char type consistency within a word. "); - // TODO(daria): remove this param when ngram permuter is deprecated. - double_VAR_H(segment_reward_ngram_best_choice, 0.99, - "Score multipler for ngram permuter's best choice" - " (only used in the Han script path)."); BOOL_VAR_H(save_doc_words, 0, "Save Document Words"); - BOOL_VAR_H(doc_dict_enable, 1, "Enable Document Dictionary "); double_VAR_H(doc_dict_pending_threshold, 0.0, "Worst certainty for using pending dictionary"); double_VAR_H(doc_dict_certainty_threshold, -2.25, "Worst certainty" " for words that can be inserted into the document dictionary"); - BOOL_VAR_H(ngram_permuter_activated, false, - "Activate character-level n-gram-based permuter"); INT_VAR_H(max_permuter_attempts, 10000, "Maximum number of different" - " character choices to consider during permutation." - " This limit is especially useful when user patterns" - " are specified, since overly generic patterns can result in" - " dawg search exploring an overly large number of options."); - BOOL_VAR_H(permute_only_top, false, "Run only the top choice permuter"); + " character choices to consider during permutation." + " This limit is especially useful when user patterns" + " are specified, since overly generic patterns can result in" + " dawg search exploring an overly large number of options."); }; } // namespace tesseract diff --git a/dict/hyphen.cpp b/dict/hyphen.cpp index 9f4296e83a..497e282d47 100644 --- a/dict/hyphen.cpp +++ b/dict/hyphen.cpp @@ -35,7 +35,6 @@ void Dict::reset_hyphen_vars(bool last_word_on_line) { delete hyphen_word_; hyphen_word_ = NULL; hyphen_active_dawgs_.clear(); - hyphen_constraints_.clear(); } } if (hyphen_debug_level) { @@ -45,11 +44,10 @@ void Dict::reset_hyphen_vars(bool last_word_on_line) { last_word_on_line_ = last_word_on_line; } -// Update hyphen_word_, and copy the given DawgInfoVectors into -// hyphen_active_dawgs_ and hyphen_constraints_. +// Update hyphen_word_, and copy the given DawgPositionVectors into +// hyphen_active_dawgs_. void Dict::set_hyphen_word(const WERD_CHOICE &word, - const DawgInfoVector &active_dawgs, - const DawgInfoVector &constraints) { + const DawgPositionVector &active_dawgs) { if (hyphen_word_ == NULL) { hyphen_word_ = new WERD_CHOICE(word.unicharset()); hyphen_word_->make_bad(); @@ -60,7 +58,6 @@ void Dict::set_hyphen_word(const WERD_CHOICE &word, // any unichar_string/lengths that are present. hyphen_word_->remove_last_unichar_id(); hyphen_active_dawgs_ = active_dawgs; - hyphen_constraints_ = constraints; } if (hyphen_debug_level) { hyphen_word_->print("set_hyphen_word: "); diff --git a/dict/permdawg.cpp b/dict/permdawg.cpp index 4219845f7a..bb1f87a4a7 100644 --- a/dict/permdawg.cpp +++ b/dict/permdawg.cpp @@ -31,7 +31,6 @@ #include "freelist.h" #include "globals.h" #include "ndminx.h" -#include "permute.h" #include "stopper.h" #include "tprintf.h" #include "params.h" @@ -45,24 +44,11 @@ ----------------------------------------------------------------------*/ namespace tesseract { -static const float kPermDawgRatingPad = 5.0; - /** * @name go_deeper_dawg_fxn * * If the choice being composed so far could be a dictionary word * keep exploring choices. - * - * There are two modes for deciding whether to go deeper: regular dawg - * permuter mode and the special ambigs mode. If *limit is <= 0.0 the - * function switches to the ambigs mode (this is the case when - * dawg_permute_and_select() function is called from NoDangerousAmbigs()) and - * only searches for the first choice that has a rating better than *limit - * (in this case ratings are fake, since the real ratings can not be < 0). - * Modification of the hyphen state is turned off in the ambigs mode. - * When in the regular dawg permuter mode, the function explores all the - * possible words and chooses the one with the best rating. The letters with - * ratings that are far worse than the ones seen so far are pruned out. */ void Dict::go_deeper_dawg_fxn( const char *debug, const BLOB_CHOICE_LIST_VECTOR &char_choices, @@ -70,182 +56,114 @@ void Dict::go_deeper_dawg_fxn( bool word_ending, WERD_CHOICE *word, float certainties[], float *limit, WERD_CHOICE *best_choice, int *attempts_left, void *void_more_args) { DawgArgs *more_args = reinterpret_cast(void_more_args); - word_ending = (char_choice_index == more_args->end_char_choice_index); + word_ending = (char_choice_index == char_choices.size()-1); int word_index = word->length() - 1; + if (best_choice->rating() < *limit) return; + // Look up char in DAWG - if (ambigs_mode(*limit)) { - if (best_choice->rating() < *limit) return; - } else { - // Prune bad subwords - if (more_args->rating_array[word_index] == NO_RATING) { - more_args->rating_array[word_index] = word->rating(); - } else { - float permdawg_limit = more_args->rating_array[word_index] * - more_args->rating_margin + kPermDawgRatingPad; - if (permdawg_limit < word->rating()) { - if (permute_debug && dawg_debug_level) { - tprintf("early pruned word rating=%4.2f," - " permdawg_limit=%4.2f, word=%s\n", word->rating(), - permdawg_limit, word->debug_string().string()); - } - return; - } + // If the current unichar is an ngram first try calling + // letter_is_okay() for each unigram it contains separately. + UNICHAR_ID orig_uch_id = word->unichar_id(word_index); + bool checked_unigrams = false; + if (getUnicharset().get_isngram(orig_uch_id)) { + if (dawg_debug_level) { + tprintf("checking unigrams in an ngram %s\n", + getUnicharset().debug_str(orig_uch_id).string()); } - } - // Deal with hyphens - if (word_ending && more_args->sought_word_length == kAnyWordLength && - has_hyphen_end(*word) && !ambigs_mode(*limit)) { - // Copy more_args->active_dawgs to clean_active_dawgs removing - // dawgs of type DAWG_TYPE_PATTERN. - DawgInfoVector clean_active_dawgs; - const DawgInfoVector &active_dawgs = *(more_args->active_dawgs); - for (int i = 0; i < active_dawgs.size(); ++i) { - if (dawgs_[active_dawgs[i].dawg_index]->type() != DAWG_TYPE_PATTERN) { - clean_active_dawgs += active_dawgs[i]; + int num_unigrams = 0; + word->remove_last_unichar_id(); + GenericVector encoding; + const char *ngram_str = getUnicharset().id_to_unichar(orig_uch_id); + // Since the string came out of the unicharset, failure is impossible. + ASSERT_HOST(getUnicharset().encode_string(ngram_str, true, &encoding, NULL, + NULL)); + bool unigrams_ok = true; + // Construct DawgArgs that reflect the current state. + DawgPositionVector unigram_active_dawgs = *(more_args->active_dawgs); + DawgPositionVector unigram_updated_dawgs; + DawgArgs unigram_dawg_args(&unigram_active_dawgs, + &unigram_updated_dawgs, + more_args->permuter); + // Check unigrams in the ngram with letter_is_okay(). + for (int i = 0; unigrams_ok && i < encoding.size(); ++i) { + UNICHAR_ID uch_id = encoding[i]; + ASSERT_HOST(uch_id != INVALID_UNICHAR_ID); + ++num_unigrams; + word->append_unichar_id(uch_id, 1, 0.0, 0.0); + unigrams_ok = (this->*letter_is_okay_)( + &unigram_dawg_args, + word->unichar_id(word_index+num_unigrams-1), + word_ending && i == encoding.size() - 1); + (*unigram_dawg_args.active_dawgs) = *(unigram_dawg_args.updated_dawgs); + if (dawg_debug_level) { + tprintf("unigram %s is %s\n", + getUnicharset().debug_str(uch_id).string(), + unigrams_ok ? "OK" : "not OK"); } } - if (clean_active_dawgs.size() > 0) { - if (permute_debug && dawg_debug_level) - tprintf("new hyphen choice = %s\n", word->debug_string().string()); - word->set_permuter(more_args->permuter); - adjust_word(word, certainties, &char_choices, permute_debug); - set_hyphen_word(*word, *(more_args->active_dawgs), - *(more_args->constraints)); - update_best_choice(*word, best_choice); + // Restore the word and copy the updated dawg state if needed. + while (num_unigrams-- > 0) word->remove_last_unichar_id(); + word->append_unichar_id_space_allocated(orig_uch_id, 1, 0.0, 0.0); + if (unigrams_ok) { + checked_unigrams = true; + more_args->permuter = unigram_dawg_args.permuter; + *(more_args->updated_dawgs) = *(unigram_dawg_args.updated_dawgs); } - } else { // Look up char in DAWG - // TODO(daria): update the rest of the code that specifies alternative - // letter_is_okay_ functions (e.g. TessCharNgram class) to work with - // multi-byte unichars and/or unichar ids. + } - // If the current unichar is an ngram first try calling - // letter_is_okay() for each unigram it contains separately. - UNICHAR_ID orig_uch_id = word->unichar_id(word_index); - bool checked_unigrams = false; - if (getUnicharset().get_isngram(orig_uch_id)) { - if (permute_debug && dawg_debug_level) { - tprintf("checking unigrams in an ngram %s\n", - getUnicharset().debug_str(orig_uch_id).string()); + // Check which dawgs from the dawgs_ vector contain the word + // up to and including the current unichar. + if (checked_unigrams || (this->*letter_is_okay_)( + more_args, word->unichar_id(word_index), word_ending)) { + // Add a new word choice + if (word_ending) { + if (dawg_debug_level) { + tprintf("found word = %s\n", word->debug_string().string()); } - int orig_num_fragments = word->fragment_length(word_index); - int num_unigrams = 0; - word->remove_last_unichar_id(); - const char *ngram_str = getUnicharset().id_to_unichar(orig_uch_id); - const char *ngram_str_end = ngram_str + strlen(ngram_str); - const char *ngram_ptr = ngram_str; - bool unigrams_ok = true; - // Construct DawgArgs that reflect the current state. - DawgInfoVector unigram_active_dawgs = *(more_args->active_dawgs); - DawgInfoVector unigram_constraints = *(more_args->constraints); - DawgInfoVector unigram_updated_active_dawgs; - DawgInfoVector unigram_updated_constraints; - DawgArgs unigram_dawg_args(&unigram_active_dawgs, - &unigram_constraints, - &unigram_updated_active_dawgs, - &unigram_updated_constraints, 0.0, - more_args->permuter, - more_args->sought_word_length, - more_args->end_char_choice_index); - // Check unigrams in the ngram with letter_is_okay(). - while (unigrams_ok && ngram_ptr < ngram_str_end) { - int step = getUnicharset().step(ngram_ptr); - UNICHAR_ID uch_id = (step <= 0) ? INVALID_UNICHAR_ID : - getUnicharset().unichar_to_id(ngram_ptr, step); - ngram_ptr += step; - ++num_unigrams; - word->append_unichar_id(uch_id, 1, 0.0, 0.0); - unigrams_ok = unigrams_ok && (this->*letter_is_okay_)( - &unigram_dawg_args, - word->unichar_id(word_index+num_unigrams-1), - word_ending && (ngram_ptr == ngram_str_end)); - (*unigram_dawg_args.active_dawgs) = - *(unigram_dawg_args.updated_active_dawgs); - (*unigram_dawg_args.constraints) = - *(unigram_dawg_args.updated_constraints); - if (permute_debug && dawg_debug_level) { - tprintf("unigram %s is %s\n", - getUnicharset().debug_str(uch_id).string(), - unigrams_ok ? "OK" : "not OK"); - } - } - // Restore the word and copy the updated dawg state if needed. - while (num_unigrams-- > 0) word->remove_last_unichar_id(); - word->append_unichar_id_space_allocated( - orig_uch_id, orig_num_fragments, 0.0, 0.0); - if (unigrams_ok) { - checked_unigrams = true; - more_args->permuter = unigram_dawg_args.permuter; - *(more_args->updated_active_dawgs) = - *(unigram_dawg_args.updated_active_dawgs); - *(more_args->updated_constraints) = - *(unigram_dawg_args.updated_constraints); - } - } - - // Check which dawgs from the dawgs_ vector contain the word - // up to and including the current unichar. - if (checked_unigrams || (this->*letter_is_okay_)( - more_args, word->unichar_id(word_index), word_ending)) { - // Add a new word choice - if (word_ending) { - if (permute_debug && dawg_debug_level) { - tprintf("found word = %s\n", word->debug_string().string()); - } - if (ambigs_mode(*limit) && - strcmp(output_ambig_words_file.string(), "") != 0) { + if (strcmp(output_ambig_words_file.string(), "") != 0) { + if (output_ambig_words_file_ == NULL) { + output_ambig_words_file_ = + fopen(output_ambig_words_file.string(), "wb+"); if (output_ambig_words_file_ == NULL) { - output_ambig_words_file_ = - fopen(output_ambig_words_file.string(), "wb+"); - if (output_ambig_words_file_ == NULL) { - tprintf("Failed to open output_ambig_words_file %s\n", - output_ambig_words_file.string()); - exit(1); - } + tprintf("Failed to open output_ambig_words_file %s\n", + output_ambig_words_file.string()); + exit(1); } STRING word_str; word->string_and_lengths(&word_str, NULL); word_str += " "; - fprintf(output_ambig_words_file_, word_str.string()); - } - WERD_CHOICE *adjusted_word = word; - WERD_CHOICE hyphen_tail_word(&getUnicharset()); - if (hyphen_base_size() > 0) { - hyphen_tail_word = *word; - remove_hyphen_head(&hyphen_tail_word); - adjusted_word = &hyphen_tail_word; + fprintf(output_ambig_words_file_, "%s", word_str.string()); } - adjusted_word->set_permuter(more_args->permuter); - if (!ambigs_mode(*limit)) { - adjust_word(adjusted_word, &certainties[hyphen_base_size()], - &char_choices, permute_debug); - } - update_best_choice(*adjusted_word, best_choice); - } else { // search the next letter - // Make updated_* point to the next entries in the DawgInfoVector - // arrays (that were originally created in dawg_permute_and_select) - ++(more_args->updated_active_dawgs); - ++(more_args->updated_constraints); - // Make active_dawgs and constraints point to the updated ones. - ++(more_args->active_dawgs); - ++(more_args->constraints); - permute_choices(debug, char_choices, char_choice_index + 1, - prev_char_frag_info, word, certainties, limit, - best_choice, attempts_left, more_args); - // Restore previous state to explore another letter in this position. - --(more_args->updated_active_dawgs); - --(more_args->updated_constraints); - --(more_args->active_dawgs); - --(more_args->constraints); + STRING word_str; + word->string_and_lengths(&word_str, NULL); + word_str += " "; + fprintf(output_ambig_words_file_, "%s", word_str.string()); } - } else { - if (permute_debug && dawg_debug_level) { + WERD_CHOICE *adjusted_word = word; + adjusted_word->set_permuter(more_args->permuter); + update_best_choice(*adjusted_word, best_choice); + } else { // search the next letter + // Make updated_* point to the next entries in the DawgPositionVector + // arrays (that were originally created in dawg_permute_and_select) + ++(more_args->updated_dawgs); + // Make active_dawgs and constraints point to the updated ones. + ++(more_args->active_dawgs); + permute_choices(debug, char_choices, char_choice_index + 1, + prev_char_frag_info, word, certainties, limit, + best_choice, attempts_left, more_args); + // Restore previous state to explore another letter in this position. + --(more_args->updated_dawgs); + --(more_args->active_dawgs); + } + } else { + if (dawg_debug_level) { tprintf("last unichar not OK at index %d in %s\n", word_index, word->debug_string().string()); - } } } } + /** * dawg_permute_and_select * @@ -253,61 +171,230 @@ void Dict::go_deeper_dawg_fxn( * the given char_choices. Use go_deeper_dawg_fxn() to search all the * dawgs in the dawgs_ vector in parallel and discard invalid words. * - * If sought_word_length is not kAnyWordLength, the function only searches - * for a valid word formed by the given char_choices in one fixed length - * dawg (that contains words of length sought_word_length) starting at the - * start_char_choice_index. - * * Allocate and return a WERD_CHOICE with the best valid word found. */ WERD_CHOICE *Dict::dawg_permute_and_select( - const BLOB_CHOICE_LIST_VECTOR &char_choices, float rating_limit, - int sought_word_length, int start_char_choice_index) { + const BLOB_CHOICE_LIST_VECTOR &char_choices, float rating_limit) { WERD_CHOICE *best_choice = new WERD_CHOICE(&getUnicharset()); best_choice->make_bad(); best_choice->set_rating(rating_limit); - if (char_choices.length() == 0) return best_choice; - DawgInfoVector *active_dawgs = new DawgInfoVector[char_choices.length() + 1]; - DawgInfoVector *constraints = new DawgInfoVector[char_choices.length() + 1]; - init_active_dawgs(sought_word_length, &(active_dawgs[0]), - ambigs_mode(rating_limit)); - init_constraints(&(constraints[0])); - int end_char_choice_index = (sought_word_length == kAnyWordLength) ? - char_choices.length()-1 : start_char_choice_index+sought_word_length-1; - // Need to skip accumulating word choices if we are only searching a part of - // the word (e.g. for the phrase search in non-space delimited languages). - // Also need to skip accumulating choices if char_choices are expanded - // with ambiguities. - bool re_enable_choice_accum = ChoiceAccumEnabled(); - if (sought_word_length != kAnyWordLength || - ambigs_mode(rating_limit)) DisableChoiceAccum(); - DawgArgs dawg_args(&(active_dawgs[0]), &(constraints[0]), - &(active_dawgs[1]), &(constraints[1]), - (segment_penalty_dict_case_bad / - segment_penalty_dict_case_ok), - NO_PERM, sought_word_length, end_char_choice_index); + if (char_choices.length() == 0 || char_choices.length() > MAX_WERD_LENGTH) + return best_choice; + DawgPositionVector *active_dawgs = + new DawgPositionVector[char_choices.length() + 1]; + init_active_dawgs(&(active_dawgs[0]), true); + DawgArgs dawg_args(&(active_dawgs[0]), &(active_dawgs[1]), NO_PERM); WERD_CHOICE word(&getUnicharset(), MAX_WERD_LENGTH); - copy_hyphen_info(&word); - // Discard rating and certainty of the hyphen base (if any). - word.set_rating(0.0); - word.set_certainty(0.0); - if (word.length() + char_choices.length() > MAX_WERD_LENGTH) { - delete[] active_dawgs; - delete[] constraints; - return best_choice; // the word is too long to permute - } + float certainties[MAX_WERD_LENGTH]; this->go_deeper_fxn_ = &tesseract::Dict::go_deeper_dawg_fxn; int attempts_left = max_permuter_attempts; - permute_choices((permute_debug && dawg_debug_level) ? - "permute_dawg_debug" : NULL, - char_choices, start_char_choice_index, NULL, &word, - certainties, &rating_limit, best_choice, &attempts_left, - &dawg_args); + permute_choices((dawg_debug_level) ? "permute_dawg_debug" : NULL, + char_choices, 0, NULL, &word, certainties, &rating_limit, best_choice, + &attempts_left, &dawg_args); delete[] active_dawgs; - delete[] constraints; - if (re_enable_choice_accum) EnableChoiceAccum(); return best_choice; } +/** + * permute_choices + * + * Call append_choices() for each BLOB_CHOICE in BLOB_CHOICE_LIST + * with the given char_choice_index in char_choices. + */ +void Dict::permute_choices( + const char *debug, + const BLOB_CHOICE_LIST_VECTOR &char_choices, + int char_choice_index, + const CHAR_FRAGMENT_INFO *prev_char_frag_info, + WERD_CHOICE *word, + float certainties[], + float *limit, + WERD_CHOICE *best_choice, + int *attempts_left, + void *more_args) { + if (debug) { + tprintf("%s permute_choices: char_choice_index=%d" + " limit=%g rating=%g, certainty=%g word=%s\n", + debug, char_choice_index, *limit, word->rating(), + word->certainty(), word->debug_string().string()); + } + if (char_choice_index < char_choices.length()) { + BLOB_CHOICE_IT blob_choice_it; + blob_choice_it.set_to_list(char_choices.get(char_choice_index)); + for (blob_choice_it.mark_cycle_pt(); !blob_choice_it.cycled_list(); + blob_choice_it.forward()) { + (*attempts_left)--; + append_choices(debug, char_choices, *(blob_choice_it.data()), + char_choice_index, prev_char_frag_info, word, + certainties, limit, best_choice, attempts_left, more_args); + if (*attempts_left <= 0) { + if (debug) tprintf("permute_choices(): attempts_left is 0\n"); + break; + } + } + } +} + +/** + * append_choices + * + * Checks to see whether or not the next choice is worth appending to + * the word being generated. If so then keeps going deeper into the word. + * + * This function assumes that Dict::go_deeper_fxn_ is set. + */ +void Dict::append_choices( + const char *debug, + const BLOB_CHOICE_LIST_VECTOR &char_choices, + const BLOB_CHOICE &blob_choice, + int char_choice_index, + const CHAR_FRAGMENT_INFO *prev_char_frag_info, + WERD_CHOICE *word, + float certainties[], + float *limit, + WERD_CHOICE *best_choice, + int *attempts_left, + void *more_args) { + int word_ending = + (char_choice_index == char_choices.length() - 1) ? true : false; + + // Deal with fragments. + CHAR_FRAGMENT_INFO char_frag_info; + if (!fragment_state_okay(blob_choice.unichar_id(), blob_choice.rating(), + blob_choice.certainty(), prev_char_frag_info, debug, + word_ending, &char_frag_info)) { + return; // blob_choice must be an invalid fragment + } + // Search the next letter if this character is a fragment. + if (char_frag_info.unichar_id == INVALID_UNICHAR_ID) { + permute_choices(debug, char_choices, char_choice_index + 1, + &char_frag_info, word, certainties, limit, + best_choice, attempts_left, more_args); + return; + } + + // Add the next unichar. + float old_rating = word->rating(); + float old_certainty = word->certainty(); + uinT8 old_permuter = word->permuter(); + certainties[word->length()] = char_frag_info.certainty; + word->append_unichar_id_space_allocated( + char_frag_info.unichar_id, char_frag_info.num_fragments, + char_frag_info.rating, char_frag_info.certainty); + + // Explore the next unichar. + (this->*go_deeper_fxn_)(debug, char_choices, char_choice_index, + &char_frag_info, word_ending, word, certainties, + limit, best_choice, attempts_left, more_args); + + // Remove the unichar we added to explore other choices in it's place. + word->remove_last_unichar_id(); + word->set_rating(old_rating); + word->set_certainty(old_certainty); + word->set_permuter(old_permuter); +} + +/** + * @name fragment_state + * + * Given the current char choice and information about previously seen + * fragments, determines whether adjacent character fragments are + * present and whether they can be concatenated. + * + * The given prev_char_frag_info contains: + * - fragment: if not NULL contains information about immediately + * preceeding fragmented character choice + * - num_fragments: number of fragments that have been used so far + * to construct a character + * - certainty: certainty of the current choice or minimum + * certainty of all fragments concatenated so far + * - rating: rating of the current choice or sum of fragment + * ratings concatenated so far + * + * The output char_frag_info is filled in as follows: + * - character: is set to be NULL if the choice is a non-matching + * or non-ending fragment piece; is set to unichar of the given choice + * if it represents a regular character or a matching ending fragment + * - fragment,num_fragments,certainty,rating are set as described above + * + * @returns false if a non-matching fragment is discovered, true otherwise. + */ +bool Dict::fragment_state_okay(UNICHAR_ID curr_unichar_id, + float curr_rating, float curr_certainty, + const CHAR_FRAGMENT_INFO *prev_char_frag_info, + const char *debug, int word_ending, + CHAR_FRAGMENT_INFO *char_frag_info) { + const CHAR_FRAGMENT *this_fragment = + getUnicharset().get_fragment(curr_unichar_id); + const CHAR_FRAGMENT *prev_fragment = + prev_char_frag_info != NULL ? prev_char_frag_info->fragment : NULL; + + // Print debug info for fragments. + if (debug && (prev_fragment || this_fragment)) { + tprintf("%s check fragments: choice=%s word_ending=%d\n", debug, + getUnicharset().debug_str(curr_unichar_id).string(), + word_ending); + if (prev_fragment) { + tprintf("prev_fragment %s\n", prev_fragment->to_string().string()); + } + if (this_fragment) { + tprintf("this_fragment %s\n", this_fragment->to_string().string()); + } + } + + char_frag_info->unichar_id = curr_unichar_id; + char_frag_info->fragment = this_fragment; + char_frag_info->rating = curr_rating; + char_frag_info->certainty = curr_certainty; + char_frag_info->num_fragments = 1; + if (prev_fragment && !this_fragment) { + if (debug) tprintf("Skip choice with incomplete fragment\n"); + return false; + } + if (this_fragment) { + // We are dealing with a fragment. + char_frag_info->unichar_id = INVALID_UNICHAR_ID; + if (prev_fragment) { + if (!this_fragment->is_continuation_of(prev_fragment)) { + if (debug) tprintf("Non-matching fragment piece\n"); + return false; + } + if (this_fragment->is_ending()) { + char_frag_info->unichar_id = + getUnicharset().unichar_to_id(this_fragment->get_unichar()); + char_frag_info->fragment = NULL; + if (debug) { + tprintf("Built character %s from fragments\n", + getUnicharset().debug_str( + char_frag_info->unichar_id).string()); + } + } else { + if (debug) tprintf("Record fragment continuation\n"); + char_frag_info->fragment = this_fragment; + } + // Update certainty and rating. + char_frag_info->rating = + prev_char_frag_info->rating + curr_rating; + char_frag_info->num_fragments = prev_char_frag_info->num_fragments + 1; + char_frag_info->certainty = + MIN(curr_certainty, prev_char_frag_info->certainty); + } else { + if (this_fragment->is_beginning()) { + if (debug) tprintf("Record fragment beginning\n"); + } else { + if (debug) { + tprintf("Non-starting fragment piece with no prev_fragment\n"); + } + return false; + } + } + } + if (word_ending && char_frag_info->fragment) { + if (debug) tprintf("Word can not end with a fragment\n"); + return false; + } + return true; +} + } // namespace tesseract diff --git a/dict/permute.cpp b/dict/permute.cpp deleted file mode 100644 index 2ded73dddf..0000000000 --- a/dict/permute.cpp +++ /dev/null @@ -1,1565 +0,0 @@ -/* -*-C-*- - ******************************************************************************** - * - * File: permute.c (Formerly permute.c) - * Description: Choose OCR text given character-probability maps - * for sequences of glyph fragments and a dictionary provided as - * a Dual Acyclic Word Graph. - * In this file, "permute" should be read "combine." - * Author: Mark Seaman, OCR Technology - * Created: Fri Sep 22 14:05:51 1989 - * Modified: Thu Jan 3 16:38:46 1991 (Mark Seaman) marks@hpgrlt - * Language: C - * Package: N/A - * Status: Experimental (Do Not Distribute) - * - * (c) Copyright 1989, Hewlett-Packard Company. - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** http://www.apache.org/licenses/LICENSE-2.0 - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - * - *********************************************************************************/ -/*---------------------------------------------------------------------- - I n c l u d e s ----------------------------------------------------------------------*/ - -#ifdef _MSC_VER -#pragma warning(disable:4244) // Conversion warnings -#pragma warning(disable:4800) // int/bool warnings -#endif - -#include -#include - -#include "const.h" - -#include "permute.h" - -#include "callcpp.h" -#include "ccutil.h" -#include "dict.h" -#include "freelist.h" -#include "helpers.h" -#include "image.h" -#include "globals.h" -#include "ndminx.h" -#include "ratngs.h" -#include "stopper.h" -#include "tprintf.h" -#include "trie.h" -#include "params.h" -#include "unicharset.h" - - -namespace tesseract { - -/*---------------------------------------------------------------------- - F u n c t i o n s -----------------------------------------------------------------------*/ - -/** - * get_best_delete_other - * - * Returns the best of two choices and deletes the other (worse) choice. - * A choice is better if it has a non-empty string and has a lower - * rating than the other choice. If the ratings are the same, - * choice2 is preferred over choice1. - */ -WERD_CHOICE *get_best_delete_other(WERD_CHOICE *choice1, - WERD_CHOICE *choice2) { - if (!choice1) return choice2; - if (!choice2) return choice1; - if (choice1->rating() < choice2->rating() || choice2->length() == 0) { - delete choice2; - return choice1; - } else { - delete choice1; - return choice2; - } -} - -/** - * Returns the n-th choice in the given blob_list (top-K choices). - * If n > K, the last choice is returned. - */ -BLOB_CHOICE* get_nth_choice(BLOB_CHOICE_LIST* blob_list, int n) { - BLOB_CHOICE_IT c_it(blob_list); - while (n-- > 0 && !c_it.at_last()) - c_it.forward(); - return c_it.data(); -} - -/** Returns the top choice char id. A helper function to make code cleaner. */ -UNICHAR_ID get_top_choice_uid(BLOB_CHOICE_LIST *blob_list) { - if (!blob_list) return INVALID_UNICHAR_ID; - BLOB_CHOICE_IT blob_choice_it(blob_list); - return (blob_choice_it.data()) ? blob_choice_it.data()->unichar_id() - : INVALID_UNICHAR_ID; -} - -/** - * Returns the rank (starting at 0) of a given unichar ID in the char - * choice list, or -1 if not found. - */ -int find_choice_by_uid(BLOB_CHOICE_LIST *blob_list, UNICHAR_ID target_uid) { - BLOB_CHOICE_IT c_it(blob_list); - int pos = 0; - while (1) { - if (c_it.data()->unichar_id() == target_uid) return pos; - if (c_it.at_last()) break; - c_it.forward(); - pos++; - } - return -1; -} - -/** - * Returns a WERD formed by taking the specified position (nth choice) string - * from char_choices starting at the given position. - * For example, if start_pos=2, pos_str="0121" will form a word using the - * 1st choice of char 3, 2nd choice of char 4, 3rd choice of char 5, 2nd choice - * of char 6. If n > number of choice, the closest (last) one is used. - */ -WERD_CHOICE* get_choice_from_posstr(const UNICHARSET *unicharset, - const BLOB_CHOICE_LIST_VECTOR &char_choices, - int start_pos, - const char* pos_str, - float *certainties) { - int pos_str_len = strlen(pos_str); - WERD_CHOICE* wchoice = new WERD_CHOICE(unicharset); - if (start_pos + pos_str_len > char_choices.length()) { - wchoice->make_bad(); - return wchoice; - } - for (int x = 0; x < pos_str_len; x++) { - int pos = pos_str[x]-'0'; - if (pos < 0) pos = 0; // use the top choice by default, eg. '.' - if (pos >= 10) - tprintf("PosStr[%d](%d)=%c %d\n", x, pos_str_len, pos_str[x], pos); - ASSERT_HOST(pos < 10); - BLOB_CHOICE* blob_it = get_nth_choice(char_choices.get(start_pos+x), pos); - wchoice->set_permuter(NO_PERM); - wchoice->append_unichar_id(blob_it->unichar_id(), 1, - blob_it->rating(), - blob_it->certainty()); - if (certainties != NULL) certainties[x] = blob_it->certainty(); - } - return wchoice; -} - -/** - * Given a WERD_CHOICE, find the corresponding position string from - * char_choices. Pos_str must have been allocated already. - * This is the reverse of get_choice_from_posstr. - */ -void get_posstr_from_choice(const BLOB_CHOICE_LIST_VECTOR &char_choices, - WERD_CHOICE* word_choice, - int start_pos, - char* pos_str) { - for (int i = 0; i < word_choice->length(); i++) { - UNICHAR_ID target_id = word_choice->unichar_id(i); - BLOB_CHOICE_LIST* blob_choice_list = char_choices.get(start_pos + i); - int pos = find_choice_by_uid(blob_choice_list, target_id); - if (pos < 0) pos = 0; - pos_str[i] = pos + '0'; - } - pos_str[word_choice->length()] = '\0'; -} - -/** - * Iterate through all the character choices (for a single blob) and - * return the first that matches the given type, which is one of 'aA0px*', - * for lower, upper, digit, punctuation, other, and 'any', respectively. - * If not match is found, a NULL is returned. - */ -BLOB_CHOICE* find_choice_by_type( - BLOB_CHOICE_LIST *blob_choices, - char target_type, - const UNICHARSET &unicharset) { - BLOB_CHOICE_IT c_it(blob_choices); - for (c_it.mark_cycle_pt(); !c_it.cycled_list(); c_it.forward()) { - if (c_it.data() && - unicharset.get_chartype(c_it.data()->unichar_id()) == target_type) - return c_it.data(); - } - return NULL; -} - -/** - * Iterate through all the character choices (for a single blob) and - * return the first that matches the target script ID. If backup_sid - * is not 0, then a match on either the target or backup sid is allowed. - * Note that there is no preference between a target or backup sid. - * To search for another sid only if no target_sid matched, use - * secondary_sid. - * So for example, to find first Han or Common char choice, do - * find_choice_by_script(cchoice, han_sid, common_sid, 0); - * To find first Han choice, but allow Common if none is found, do - * find_choice_by_script(cchoice, han_sid, 0, common_sid); - */ -BLOB_CHOICE* find_choice_by_script( - BLOB_CHOICE_LIST *blob_choices, - int target_sid, - int backup_sid, - int secondary_sid) { - BLOB_CHOICE_IT c_it(blob_choices); - for (c_it.mark_cycle_pt(); !c_it.cycled_list(); c_it.forward()) { - bool found = false; - if (c_it.data()->script_id() == 0) continue; - if (c_it.data()->script_id() == target_sid) found = true; - if (backup_sid > 0 && c_it.data()->script_id() == backup_sid) found = true; - if (found) return c_it.data(); - } - if (secondary_sid > 0) { - c_it.set_to_list(blob_choices); - for (c_it.mark_cycle_pt(); !c_it.cycled_list(); c_it.forward()) { - if (c_it.data()->script_id() == 0) continue; - if (c_it.data()->script_id() == secondary_sid) - return c_it.data(); - } - } - return NULL; -} - - -PermuterState::PermuterState() { - unicharset_ = NULL; - char_choices_ = NULL; - adjust_factor_ = 1.0f; - allow_collision_ = false; - word_length_ = 0; - debug_ = false; -} - -void PermuterState::Init(const BLOB_CHOICE_LIST_VECTOR& char_choices, - const UNICHARSET& unicharset, - float default_bias, - bool debug) { - ASSERT_HOST(char_choices.length() < MAX_PERM_LENGTH); - unicharset_ = &unicharset; - char_choices_ = &char_choices; - word_length_ = char_choices.length(); - for (int i = 0; i < word_length_; ++i) - perm_state_[i] = kPosFree; - perm_state_[word_length_] = '\0'; - // Skip fragment choice and the mark positions so they remain unchanged. - for (int i = 0; i < word_length_; ++i) { - UNICHAR_ID unichar_id = get_top_choice_uid(char_choices.get(i)); - if (unicharset.get_fragment(unichar_id) != NULL) - perm_state_[i] = '1'; - } - adjust_factor_ = default_bias; - allow_collision_ = false; - debug_ = debug; -} - -// Promote char positions specified in pos_str with given weight. -// For example, AddPreference(5, "234", 0.95f) -// would promote the 3rd, 4th and 5th choice for character 5, 6, 7 -// (starting at 0) in the word with a rating adjustment of 0.95. -void PermuterState::AddPreference(int start_pos, char* pos_str, float weight) { - ASSERT_HOST(char_choices_ != NULL); - ASSERT_HOST(start_pos + strlen(pos_str) - 1 < word_length_); - if (debug_) { - tprintf("Copy over %s -> %s @ %d ", pos_str, perm_state_, start_pos); - } - // copy over preferred position without the terminating null - if (!allow_collision_) { - int len = strlen(pos_str); - for (int i = 0; i < len; ++i) - if (position_marked(start_pos + i)) return; - } - strncpy(&perm_state_[start_pos], pos_str, strlen(pos_str)); - adjust_factor_ *= weight; - if (debug_) tprintf("==> %s %f\n", perm_state_, adjust_factor_); -} - -// Promote the input blob_choice at specified position with given weight. -void PermuterState::AddPreference(int char_pos, BLOB_CHOICE* blob_choice, - float weight) { - ASSERT_HOST(char_choices_ != NULL); - ASSERT_HOST(char_pos < word_length_); - // avoid collision (but this doesn't work if the first choice is favored! - if (!allow_collision_ && position_marked(char_pos)) return; - - if (debug_) { - tprintf("Set UID %d -> %s @ %d ", - blob_choice->unichar_id(), perm_state_, char_pos); - } - int pos = find_choice_by_uid(char_choices_->get(char_pos), - blob_choice->unichar_id()); - perm_state_[char_pos] = pos + '0'; - adjust_factor_ *= weight; - if (debug_) tprintf("==> %s %f\n", perm_state_, adjust_factor_); -} - -// Get the best word permutation so far. Caller should destroy WERD_CHOICE. -WERD_CHOICE* PermuterState::GetPermutedWord(float *certainties, - float *adjust_factor) { - ASSERT_HOST(char_choices_ != NULL); - WERD_CHOICE *word_choice = get_choice_from_posstr( - unicharset_, *char_choices_, 0, perm_state_, certainties); - float rating = word_choice->rating() * adjust_factor_; - word_choice->set_rating(rating); - *adjust_factor = adjust_factor_; - return word_choice; -} - - -/********************************************************************** - * permute_all - * - * Permute all the characters together using all of the different types - * of permuters/selectors available. Each of the characters must have - * a non-NULL choice list. - * - * Note: order of applying permuters does matter, since the latter - * permuter will be recorded if the resulting word ratings are the same. - * - * Note: the function assumes that best_choice and raw_choice are not NULL. - * - * Note: Since permuter_all maybe called recursively (through permuter_ - * compound_words), there must be a separate instance of permuter_state - * for each invocation. - **********************************************************************/ -WERD_CHOICE *Dict::permute_all(const BLOB_CHOICE_LIST_VECTOR &char_choices, - const WERD_CHOICE *best_choice, - WERD_CHOICE *raw_choice) { - WERD_CHOICE *result1 = NULL; - WERD_CHOICE *result2 = NULL; - BOOL8 any_alpha; - float top_choice_rating_limit = best_choice->rating(); - int word_script_id = get_top_word_script(char_choices, getUnicharset()); - - PermuterState permuter_state; - if (getUnicharset().han_sid() != getUnicharset().null_sid() && - word_script_id == getUnicharset().han_sid()) { - permuter_state.Init(char_choices, getUnicharset(), 1.0f, permute_debug); - - result1 = get_top_choice_word(char_choices); - - // Note that we no longer need the returned word from these permuters, - // except to delete the memory. The word choice from all permutations - // is returned by permuter_state.GetpermutedWord() at the end. - if (permute_fixed_length_dawg) { - result2 = permute_fixed_length_words(char_choices, &permuter_state); - delete result2; - } - if (permute_chartype_word) { - result2 = permute_chartype_words(char_choices, &permuter_state); - delete result2; - } - if (permute_script_word) { - result2 = permute_script_words(char_choices, &permuter_state); - delete result2; - } - - float certainties[MAX_PERM_LENGTH]; - float adjust_factor; - result2 = permuter_state.GetPermutedWord(certainties, &adjust_factor); - LogNewChoice(adjust_factor, certainties, false, result2, char_choices); - result1 = get_best_delete_other(result1, result2); - - if (segment_segcost_rating) incorporate_segcost(result1); - } else { - result1 = permute_top_choice(char_choices, &top_choice_rating_limit, - raw_choice, &any_alpha); - if (result1 == NULL) - return (NULL); - if (permute_only_top) - return result1; - - if (permute_chartype_word) { - permuter_state.Init(char_choices, getUnicharset(), - segment_penalty_garbage, permute_debug); - result2 = permute_chartype_words(char_choices, &permuter_state); - result1 = get_best_delete_other(result1, result2); - } - - // Permute character fragments if necessary. - if (result1 == NULL || result1->fragment_mark()) { - result2 = top_fragments_permute_and_select(char_choices, - top_choice_rating_limit); - result1 = get_best_delete_other(result1, result2); - } - - result2 = dawg_permute_and_select(char_choices, best_choice->rating()); - result1 = get_best_delete_other(result1, result2); - - result2 = permute_compound_words(char_choices, best_choice->rating()); - result1 = get_best_delete_other(result1, result2); - } - return result1; -} - -/** - * Incorporate segmentation cost into the word rating. This is done - * through a multiplier wordseg_rating_adjust_factor_ which is determined - * in bestfirst.cpp during state evaluation. This is not the cleanest - * way to do this. It would be better to reorganize the SEARCH_STATE - * to keep track of associated states, or do the rating adjustment - * outside the permuter in evalaute_state. - */ -void Dict::incorporate_segcost(WERD_CHOICE *word) { - if (!word || wordseg_rating_adjust_factor_ <= 0) return; - - float old_rating = word->rating(); - float new_rating = old_rating * wordseg_rating_adjust_factor_; - word->set_rating(new_rating); - if (permute_debug) - tprintf("Permute segadjust %f * %f --> %f\n", - old_rating, wordseg_rating_adjust_factor_, new_rating); -} - - -/** - * Perform search on fixed-length dictionaries within a word. - * This is used for non-space delimited languages like CJK when a "word" - * corresponds to a "phrase" consisted of multiple short words. - * It iterates over every character position looking for longest matches - * against a set of fixed-length dawgs. Each dictionary hit is rewarded - * with a rating bonus. - * Note: this is very slow as it is performed on every segmentation state. - */ -WERD_CHOICE* Dict::permute_fixed_length_words( - const BLOB_CHOICE_LIST_VECTOR &char_choices, - PermuterState *permuter_state) { - if (permute_debug) - print_char_choices_list("\n\nPermute FixedLength Word", - char_choices, getUnicharset(), false); - WERD_CHOICE* best_choice = - new WERD_CHOICE(&getUnicharset(), char_choices.length()); - const int max_dict_len = max_fixed_length_dawgs_wdlen_; - const int min_dict_len = 2; - char posstr[256]; - int match_score = 0; - int anchor_pos = 0; - while (anchor_pos < char_choices.length()) { - // search from longest phrase to shortest, stop when we find a match - WERD_CHOICE* part_choice = NULL; - int step = max_dict_len; - while (step >= min_dict_len) { - int end_pos = anchor_pos + step - 1; - if (end_pos < char_choices.length()) { - part_choice = dawg_permute_and_select(char_choices, - 200.0, // rate limit - step, - anchor_pos); - if (part_choice->length() == step) { - if (permute_debug) - tprintf("match found at pos=%d len=%d\n%s\n", anchor_pos, step, - part_choice->unichar_string().string()); - break; - } - delete part_choice; - part_choice = NULL; - } - step--; - } - - if (part_choice && step > 1) { // found lexicon match - get_posstr_from_choice(char_choices, part_choice, anchor_pos, posstr); - float adjust_factor = pow(0.95, 1.0 + step*2.0/char_choices.length()); - if (permuter_state) - permuter_state->AddPreference(anchor_pos, posstr, adjust_factor); - match_score += step - 1; // single chars don't count - if (permute_debug) - tprintf("Promote word rating %d-len%d\n%s\n", anchor_pos, step, - part_choice->unichar_string().string()); - } else { // no lexicon match - step = 1; - part_choice = get_choice_from_posstr(&getUnicharset(), char_choices, - anchor_pos, "0", NULL); - if (permute_debug) - tprintf("Single char %d %s\n", anchor_pos, - part_choice->unichar_string().string()); - } - if (part_choice && part_choice->length() > 0) - (*best_choice) += (*part_choice); - if (part_choice) delete part_choice; - anchor_pos += step; - } - - if (match_score > 0) { - float adjust_factor = pow(0.8, // 1.0/segment_penalty_dict_nonword, - match_score * 2.0 / char_choices.length()); - float adjusted_score = best_choice->rating() * adjust_factor; - if (permute_debug) - tprintf("Adjusting score %f @ %d -> %f\n", - best_choice->rating(), match_score, adjusted_score); - best_choice->set_rating(adjusted_score); - } - if (permute_debug) - tprintf("Found Best CJK word %f: %s\n", - best_choice->rating(), best_choice->unichar_string().string()); - return best_choice; -} - - -/********************************************************************** - * Returns the dominant chartype for the word. Only the "main" chartype - * of each character is used, and a consistent chartype is defined by - * the majority chartype from non-ambiguous character positions. - * If pos_chartypes is not NULL, then the "main" chartype at each pos - * is also returned. The caller must allocate and deallocate the space. - **********************************************************************/ -char Dict::top_word_chartype(const BLOB_CHOICE_LIST_VECTOR &char_choices, - char* pos_chartypes) { - const UNICHARSET &unicharset = getUnicharset(); - const int hist_size = 128; // to contain 'A','a','0','x','p' - int chprop[hist_size]; - int x; - for (x = 0; x < hist_size; x++) chprop[x] = 0; - for (x = 0; x < char_choices.length(); ++x) { - UNICHAR_ID unichar_id = get_top_choice_uid(char_choices.get(x)); - char ctype = unicharset.get_chartype(unichar_id); - if (pos_chartypes) pos_chartypes[x] = ctype; - if (ctype == 0 || ctype == 'p') continue; - if (getUnicharAmbigs().OneToOneDefiniteAmbigs(unichar_id) != NULL) continue; - chprop[ctype]++; - if (x == 0 && ctype == 'A') // first-cap also counts as lower - chprop['a']++; - } - int max_prop = 0; - for (x = 1; x < hist_size; x++) - if (chprop[x] >= chprop[max_prop]) max_prop = x; - return (chprop[max_prop] > 0) ? max_prop : 0; -} - -/********************************************************************** - * Promote consistent character type based on neighboring characters. - * For each character position, if the top choice property is inconsistent - * with that of the word or previous character, then its likely - * subsitutions, as defined by DangAmbigs, will be examined and the one - * with a matching property will be selected. - **********************************************************************/ -WERD_CHOICE* Dict::permute_chartype_words( - const BLOB_CHOICE_LIST_VECTOR &char_choices, - PermuterState *permuter_state) { - - if (char_choices.length() >= MAX_PERM_LENGTH) - return NULL; - // Store main character property of top choice at every position - char pos_chartypes[MAX_PERM_LENGTH]; - char word_type = top_word_chartype(char_choices, pos_chartypes); - if (word_type == 0 || word_type == 'p') - return NULL; // skip if word type is punctuation or unknown - if (permute_debug) { - tprintf("\n\nPermuteCharType[%c]\n", word_type); - print_char_choices_list("", char_choices, getUnicharset(), true); - } - - WERD_CHOICE *current_word = new WERD_CHOICE(&getUnicharset()); - BLOB_CHOICE_IT blob_choice_it; - const UNICHARSET& unicharset = getUnicharset(); - bool replaced = false; // has any character choice been replaced - int prev_unambig_type = 0; // the last chartype of an unambiguous char - float certainties[MAX_PERM_LENGTH + 1]; - for (int x = 0; x < char_choices.length(); ++x) { - BLOB_CHOICE_LIST* pos_choice = char_choices.get(x); - UNICHAR_ID unichar_id = get_top_choice_uid(pos_choice); - if (unichar_id == 0) { - delete current_word; - return NULL; - } - blob_choice_it.set_to_list(pos_choice); - BLOB_CHOICE *first_choice = blob_choice_it.data(); - ASSERT_HOST(first_choice != NULL); - - const UnicharIdVector* ambig_uids = - getUnicharAmbigs().OneToOneDefiniteAmbigs(unichar_id); - bool is_ambiguous = (ambig_uids != NULL); - bool is_punct = unicharset.get_ispunctuation(unichar_id); - bool is_consistent = is_punct || - unicharset.get_chartype(unichar_id) == prev_unambig_type || - unicharset.get_chartype(unichar_id) == word_type; - bool is_fragment = getUnicharset().get_fragment(unichar_id) != NULL; - if (permute_debug) - tprintf("char[%d]:%s is_ambig %c is_punct %c is_consistent %c\n", - x, unicharset.id_to_unichar(unichar_id), - is_ambiguous?'T':'F', is_punct?'T':'F', is_consistent?'T':'F'); - - if (is_fragment) { - // Ignore any fragmented characters by skipping them to next choice - // (originally first choice). - first_choice = get_nth_choice(pos_choice, 1); - ASSERT_HOST(first_choice != NULL); - } else if (is_ambiguous && !is_consistent) { - // Check every confusable blob choice where the top choice is inconsistent - // with the character type of the previous unambiguous character. - if (permute_debug) { - tprintf("Checking %s r%g PrevCharType %c\n", - unicharset.id_to_unichar(unichar_id), - first_choice->rating(), prev_unambig_type); - print_ratings_list("\t", pos_choice, getUnicharset()); - } - BLOB_CHOICE* c_it = NULL; - if (c_it == NULL) { - c_it = find_choice_by_type(pos_choice, word_type, unicharset); - } - - // Prefer a character choice whose type is the same as the previous - // unambiguous character and the confusion appears in the ambig list. - if (c_it == NULL && prev_unambig_type > 0) { - c_it = find_choice_by_type(pos_choice, prev_unambig_type, unicharset); - if (c_it && - UnicharIdArrayUtils::find_in(*ambig_uids, c_it->unichar_id()) < 0) - c_it = NULL; - } - - // Otherwise, perfer a punctuation - if (c_it == NULL) { - c_it = find_choice_by_type(pos_choice, 'p', unicharset); - if (c_it && - UnicharIdArrayUtils::find_in(*ambig_uids, c_it->unichar_id()) < 0) - c_it = NULL; - } - - // save any preference other than the top choice - if (c_it != NULL) { - if (permute_debug) { - tprintf("Replacing %s r%g ==> %s r%g\n", - unicharset.id_to_unichar(unichar_id), first_choice->rating(), - unicharset.id_to_unichar(c_it->unichar_id()), c_it->rating()); - tprintf("\n\nPermuteCharType[%c]\n", word_type); - print_char_choices_list("", char_choices, getUnicharset(), false); - } - if (permuter_state) - permuter_state->AddPreference(x, c_it, segment_reward_chartype); - first_choice = c_it; - replaced = true; - } - } else if (!is_ambiguous && !is_punct) { - // keep the last unambiguous character type - prev_unambig_type = pos_chartypes[x]; - } - current_word->append_unichar_id(first_choice->unichar_id(), 1, - first_choice->rating(), - first_choice->certainty()); - certainties[x] = first_choice->certainty(); - } - // All permuter choices should go through adjust_non_word so the choice - // rating would be adjusted on the same scale. - adjust_non_word(current_word, certainties, &char_choices, permute_debug); - if (replaced) { - // Apply a reward multiplier on rating if an chartype permutation is made. - float rating = current_word->rating(); - current_word->set_rating(rating * segment_reward_chartype); - if (permute_debug) - current_word->print("<== permute_chartype_word **"); - } - return current_word; -} - -/** - * Try flipping characters in a word to get better script consistency. - * Similar to how upper/lower case checking is done in top_choice_permuter, - * this permuter tries to suggest a more script-consistent choice AND - * modifies the rating. So it combines both the case_ok check and - * adjust_non_word functionality. However, instead of penalizing an - * inconsistent word with a > 1 multiplier, we reward the script-consistent - * choice with a < 1 multiplier. - */ -WERD_CHOICE* Dict::permute_script_words( - const BLOB_CHOICE_LIST_VECTOR &char_choices, - PermuterState *permuter_state) { - if (char_choices.length() >= MAX_WERD_LENGTH) - return NULL; - - int word_sid = get_top_word_script(char_choices, getUnicharset()); - if (word_sid == getUnicharset().null_sid()) - return NULL; - - if (permute_debug) { - tprintf("\n\nPermuteScript %s\n", - getUnicharset().get_script_from_script_id(word_sid)); - print_char_choices_list("", char_choices, getUnicharset(), - permute_debug > 1); - } - - WERD_CHOICE *current_word = new WERD_CHOICE(&getUnicharset()); - BLOB_CHOICE_IT blob_choice_it; - bool replaced = false; - bool prev_is_consistent = false; - float certainties[MAX_PERM_LENGTH + 1]; - for (int x = 0; x < char_choices.length(); ++x) { - blob_choice_it.set_to_list(char_choices.get(x)); - BLOB_CHOICE *first_choice = blob_choice_it.data(); - if (!first_choice) { - delete current_word; - return NULL; - } - UNICHAR_ID unichar_id = first_choice->unichar_id(); - if (unichar_id == 0) { - delete current_word; - return NULL; - } - bool sid_consistent = (getUnicharset().get_script(unichar_id) == word_sid); - bool this_is_punct = getUnicharset().get_chartype(unichar_id) == 'p'; - bool is_fragment = getUnicharset().get_fragment(unichar_id) != NULL; - - if (is_fragment) { - // Ignore any fragmented characters by skipping them to next choice - // (originally first choice). - first_choice = get_nth_choice(char_choices.get(x), 1); - ASSERT_HOST(first_choice != NULL); - } else if (!sid_consistent && !this_is_punct && prev_is_consistent) { - // If the previous char is CJK, we prefer a cjk over non-cjk char - if (permute_debug) { - tprintf("Checking %s r%g\n", getUnicharset().id_to_unichar(unichar_id), - first_choice->rating()); - print_ratings_list("\t", char_choices.get(x), getUnicharset()); - } - // prefer a script consistent choice - BLOB_CHOICE* c_it = find_choice_by_script(char_choices.get(x), - word_sid, 0, 0); - // otherwise, prefer a punctuation - if (c_it == NULL) - c_it = find_choice_by_type(char_choices.get(x), 'p', getUnicharset()); - - if (c_it != NULL) { - if (permute_debug) - tprintf("Replacing %s r%g ==> %s r%g\n", - getUnicharset().id_to_unichar(unichar_id), - first_choice->rating(), - getUnicharset().id_to_unichar(c_it->unichar_id()), - c_it->rating()); - if (permuter_state) - permuter_state->AddPreference(x, c_it, segment_reward_script); - first_choice = c_it; - replaced = true; - } - } - current_word->append_unichar_id(first_choice->unichar_id(), 1, - first_choice->rating(), - first_choice->certainty()); - certainties[x] = first_choice->certainty(); - prev_is_consistent = sid_consistent; - } - // All permuter choices should go through adjust_non_word so the choice - // rating would be adjusted on the same scale. - adjust_non_word(current_word, certainties, &char_choices, permute_debug); - if (replaced) { - // Apply a reward multiplier on rating if an script permutation is made. - float rating = current_word->rating(); - current_word->set_rating(rating * segment_reward_script); - if (permute_debug) - current_word->print("<== permute_script_word **"); - } - return current_word; -} - -/** - * permute_characters - * - * Permute these characters together according to each of the different - * permuters that are enabled. - * Returns true if best_choice was updated. - */ -bool Dict::permute_characters(const BLOB_CHOICE_LIST_VECTOR &char_choices, - WERD_CHOICE *best_choice, - WERD_CHOICE *raw_choice) { - if (permute_debug) { - tprintf("\n\n\n##### Permute_Characters #######\n"); - print_char_choices_list("\n==> Input CharChoices", char_choices, - getUnicharset(), segment_debug > 1); - tprintf("\n"); - } - - if (char_choices.length() == 1 && - get_top_choice_uid(char_choices.get(0)) == 0) return false; - WERD_CHOICE *this_choice = permute_all(char_choices, best_choice, raw_choice); - - if (this_choice && this_choice->rating() < best_choice->rating()) { - *best_choice = *this_choice; - - if (permute_debug) { - best_choice->print("\n**** Populate BestChoice"); - cprintf("populate best_choice\n\t%s\n", - best_choice->debug_string().string()); - } - delete this_choice; - return true; - } - delete this_choice; - return false; -} - -/** - * permute_compound_words - * - * Return the top choice for each character as the choice for the word. - */ -WERD_CHOICE *Dict::permute_compound_words( - const BLOB_CHOICE_LIST_VECTOR &char_choices, - float rating_limit) { - BLOB_CHOICE *first_choice; - WERD_CHOICE *best_choice = NULL; - WERD_CHOICE current_word(&getUnicharset(), MAX_WERD_LENGTH); - int first_index = 0; - int x; - BLOB_CHOICE_IT blob_choice_it; - - if (char_choices.length() > MAX_WERD_LENGTH) { - WERD_CHOICE *bad_word_choice = new WERD_CHOICE(&getUnicharset()); - bad_word_choice->make_bad(); - return bad_word_choice; - } - - UNICHAR_ID slash = getUnicharset().unichar_to_id("/"); - UNICHAR_ID dash = getUnicharset().unichar_to_id("-"); - for (x = 0; x < char_choices.length(); ++x) { - blob_choice_it.set_to_list(char_choices.get(x)); - first_choice = blob_choice_it.data(); - if (first_choice->unichar_id() == slash || - first_choice->unichar_id() == dash) { - if (x > first_index) { - if (segment_debug) - cprintf ("Hyphenated word found\n"); - permute_subword(char_choices, rating_limit, first_index, - x - 1, ¤t_word); - if (current_word.rating() > rating_limit) - break; - } - // Append hyphen/slash separator to current_word. - current_word.append_unichar_id_space_allocated( - first_choice->unichar_id(), 1, - first_choice->rating(), first_choice->certainty()); - - first_index = x + 1; // update first_index - } - } - - if (first_index > 0 && first_index < x && - current_word.rating() <= rating_limit) { - permute_subword(char_choices, rating_limit, first_index, - x - 1, ¤t_word); - best_choice = new WERD_CHOICE(current_word); - best_choice->set_permuter(COMPOUND_PERM); - } - return (best_choice); -} - - -/** - * permute_subword - * - * Permute a part of a compound word this subword is bounded by hyphens - * and the start and end of the word. Call the standard word permute - * function on a set of choices covering only part of the original - * word. When it is done reclaim the memory that was used in the - * exercise. - */ -void Dict::permute_subword(const BLOB_CHOICE_LIST_VECTOR &char_choices, - float rating_limit, - int start, - int end, - WERD_CHOICE *current_word) { - int x; - BLOB_CHOICE_LIST_VECTOR subchoices; - WERD_CHOICE *best_choice = NULL; - WERD_CHOICE raw_choice(&getUnicharset()); - raw_choice.make_bad(); - - DisableChoiceAccum(); - - for (x = start; x <= end; x++) { - if (char_choices.get(x) != NULL) { - subchoices += char_choices.get(x); - } - } - - if (!subchoices.empty()) { - WERD_CHOICE initial_choice(&getUnicharset()); - initial_choice.make_bad(); - initial_choice.set_rating(rating_limit); - - best_choice = permute_all(subchoices, &initial_choice, &raw_choice); - - if (best_choice && best_choice->length() > 0) { - *current_word += *best_choice; - } else { - current_word->set_rating(MAX_FLOAT32); - } - } else { - current_word->set_rating(MAX_FLOAT32); - } - - if (best_choice) - delete best_choice; - - if (segment_debug && current_word->rating() < MAX_FLOAT32) { - cprintf ("Subword permuted = %s, %5.2f, %5.2f\n\n", - current_word->debug_string().string(), - current_word->rating(), current_word->certainty()); - } - EnableChoiceAccum(); -} - -/** - * Return the top choice for each character as the choice for the word. - */ -WERD_CHOICE *Dict::get_top_choice_word( - const BLOB_CHOICE_LIST_VECTOR &char_choices) { - WERD_CHOICE *top_word = new WERD_CHOICE(&getUnicharset(), MAX_PERM_LENGTH); - float certainties[MAX_PERM_LENGTH]; - top_word->set_permuter(TOP_CHOICE_PERM); - for (int x = 0; x < char_choices.length(); x++) { - BLOB_CHOICE_IT blob_choice_it; - blob_choice_it.set_to_list(char_choices.get(x)); - BLOB_CHOICE *top_choice = blob_choice_it.data(); - top_word->append_unichar_id_space_allocated(top_choice->unichar_id(), 1, - top_choice->rating(), - top_choice->certainty()); - certainties[x] = top_choice->certainty(); - } - LogNewChoice(1.0, certainties, true, top_word, char_choices); - return top_word; -} - -/** - * permute_top_choice - * - * Return the top choice for each character as the choice for the word. - * In addition a choice is created for the best lower and upper case - * non-words. In each character position the best lower (or upper) case - * character is substituted for the best overall character. - */ -WERD_CHOICE *Dict::permute_top_choice( - const BLOB_CHOICE_LIST_VECTOR &char_choices, - float* rating_limit, - WERD_CHOICE *raw_choice, - BOOL8 *any_alpha) { - BLOB_CHOICE *first_choice; - const char *first_char; //first choice - const char *second_char; //second choice - const char *third_char; //third choice - char prev_char[UNICHAR_LEN + 1]; //prev in word - const char *next_char = ""; //next in word - const char *next_next_char = ""; //after next next in word - - WERD_CHOICE word(&getUnicharset(), MAX_PERM_LENGTH); - word.set_permuter(TOP_CHOICE_PERM); - WERD_CHOICE capital_word(&getUnicharset(), MAX_PERM_LENGTH); - capital_word.set_permuter(UPPER_CASE_PERM); - WERD_CHOICE lower_word(&getUnicharset(), MAX_PERM_LENGTH); - lower_word.set_permuter(LOWER_CASE_PERM); - - int x; - BOOL8 char_alpha; - float first_rating = 0; - - float certainties[MAX_PERM_LENGTH + 1]; - float lower_certainties[MAX_PERM_LENGTH + 1]; - float upper_certainties[MAX_PERM_LENGTH + 1]; - - BLOB_CHOICE_IT blob_choice_it; - UNICHAR_ID temp_id; - UNICHAR_ID unichar_id; - UNICHAR_ID space = getUnicharset().unichar_to_id(" "); - register const char* ch; - register inT8 lower_done; - register inT8 upper_done; - - prev_char[0] = '\0'; - - if (any_alpha != NULL) - *any_alpha = FALSE; - - if (char_choices.length() > MAX_PERM_LENGTH) { - return (NULL); - } - - for (x = 0; x < char_choices.length(); ++x) { - if (x + 1 < char_choices.length()) { - unichar_id = get_top_choice_uid(char_choices.get(x+1)); - next_char = unichar_id != INVALID_UNICHAR_ID ? - getUnicharset().id_to_unichar(unichar_id) : ""; - } else { - next_char = ""; - } - - if (x + 2 < char_choices.length()) { - unichar_id = get_top_choice_uid(char_choices.get(x+2)); - next_next_char = unichar_id != INVALID_UNICHAR_ID ? - getUnicharset().id_to_unichar(unichar_id) : ""; - } else { - next_next_char = ""; - } - - blob_choice_it.set_to_list(char_choices.get(x)); - ASSERT_HOST(!blob_choice_it.empty()); - first_choice = NULL; - for (blob_choice_it.mark_cycle_pt(); !blob_choice_it.cycled_list(); - blob_choice_it.forward()) { // find the best non-fragment char choice - temp_id = blob_choice_it.data()->unichar_id(); - if (!(getUnicharset().get_fragment(temp_id))) { - first_choice = blob_choice_it.data(); - break; - } else if (char_choices.length() > 1) { - word.set_fragment_mark(true); - capital_word.set_fragment_mark(true); - lower_word.set_fragment_mark(true); - } - } - if (first_choice == NULL) { - cprintf("Permuter found only fragments for" - " character at position %d; word=%s\n", - x, word.debug_string().string()); - } - ASSERT_HOST(first_choice != NULL); - - unichar_id = first_choice->unichar_id() != INVALID_UNICHAR_ID ? - first_choice->unichar_id() : space; - first_char = getUnicharset().id_to_unichar(unichar_id); - first_rating = first_choice->rating(); - word.append_unichar_id_space_allocated( - unichar_id, 1, first_choice->rating(), first_choice->certainty()); - capital_word.append_unichar_id_space_allocated( - unichar_id, 1, first_choice->rating(), first_choice->certainty()); - lower_word.append_unichar_id_space_allocated( - unichar_id, 1, first_choice->rating(), first_choice->certainty()); - - certainties[x] = first_choice->certainty(); - lower_certainties[x] = first_choice->certainty(); - upper_certainties[x] = first_choice->certainty(); - - lower_done = FALSE; - upper_done = FALSE; - char_alpha = FALSE; - second_char = ""; - third_char = ""; - for (; !blob_choice_it.cycled_list(); blob_choice_it.forward()) { - unichar_id = blob_choice_it.data()->unichar_id(); - if (getUnicharset().eq(unichar_id, "l") && !blob_choice_it.at_last() && - blob_choice_it.data_relative(1)->rating() == first_rating) { - temp_id = blob_choice_it.data_relative(1)->unichar_id(); - if (getUnicharset().eq(temp_id, "1") || - getUnicharset().eq(temp_id, "I")) { - second_char = getUnicharset().id_to_unichar(temp_id); - blob_choice_it.forward(); - if (!blob_choice_it.at_last() && - blob_choice_it.data_relative(1)->rating() == first_rating) { - temp_id = blob_choice_it.data_relative(1)->unichar_id(); - if (getUnicharset().eq(temp_id, "1") || - getUnicharset().eq(temp_id, "I")) { - third_char = getUnicharset().id_to_unichar(temp_id); - blob_choice_it.forward(); - } - } - ch = choose_il1 (first_char, second_char, third_char, - prev_char, next_char, next_next_char); - unichar_id = (ch != NULL && *ch != '\0') ? - getUnicharset().unichar_to_id(ch) : INVALID_UNICHAR_ID; - if (strcmp(ch, "l") != 0 && - getUnicharset().eq(word.unichar_id(x), "l")) { - word.set_unichar_id(unichar_id, x); - lower_word.set_unichar_id(unichar_id, x); - capital_word.set_unichar_id(unichar_id, x); - } - } - } - if (unichar_id != INVALID_UNICHAR_ID) { - /* Find lower case */ - if (!lower_done && - (getUnicharset().get_islower(unichar_id) || - (getUnicharset().get_isupper(unichar_id) && x == 0))) { - lower_word.set_unichar_id(unichar_id, x); - lower_word.set_rating(lower_word.rating() - - first_choice->rating() + blob_choice_it.data()->rating()); - if (blob_choice_it.data()->certainty() < lower_word.certainty()) { - lower_word.set_certainty(blob_choice_it.data()->certainty()); - } - lower_certainties[x] = blob_choice_it.data()->certainty(); - lower_done = TRUE; - } - /* Find upper case */ - if (!upper_done && getUnicharset().get_isupper(unichar_id)) { - capital_word.set_unichar_id(unichar_id, x); - capital_word.set_rating(capital_word.rating() - - first_choice->rating() + blob_choice_it.data()->rating()); - if (blob_choice_it.data()->certainty() < capital_word.certainty()) { - capital_word.set_certainty(blob_choice_it.data()->certainty()); - } - upper_certainties[x] = blob_choice_it.data()->certainty(); - upper_done = TRUE; - } - if (!char_alpha) { - const CHAR_FRAGMENT *fragment = - getUnicharset().get_fragment(unichar_id); - temp_id = !fragment ? unichar_id : - getUnicharset().unichar_to_id(fragment->get_unichar()); - if (getUnicharset().get_isalpha(temp_id)) { - char_alpha = TRUE; - } - } - if (lower_done && upper_done) - break; - } - } - if (char_alpha && any_alpha != NULL) - *any_alpha = TRUE; - - if (word.rating() > bestrate_pruning_factor * *rating_limit) { - if (permute_debug) - tprintf("\n***** Aborting high-cost word: %g > limit %g\n", - word.rating(), bestrate_pruning_factor * *rating_limit); - return (NULL); - } - - *prev_char = '\0'; - temp_id = word.unichar_id(word.length()-1); - if (temp_id != INVALID_UNICHAR_ID) { - strcpy(prev_char, getUnicharset().id_to_unichar(temp_id)); - } - } - - if (raw_choice != NULL && word.rating() < raw_choice->rating()) { - *raw_choice = word; - LogNewChoice(1.0, certainties, true, raw_choice, char_choices); - } - float rating = word.rating(); - adjust_non_word(&word, certainties, &char_choices, permute_debug); - - float lower_rating = lower_word.rating(); - adjust_non_word(&lower_word, lower_certainties, &char_choices, - permute_debug); - - float upper_rating = capital_word.rating(); - adjust_non_word(&capital_word, upper_certainties, &char_choices, - permute_debug); - - WERD_CHOICE *best_choice = &word; - *rating_limit = rating; - if (lower_word.rating() < best_choice->rating()) { - best_choice = &lower_word; - *rating_limit = lower_rating; - } - if (capital_word.rating() < best_choice->rating()) { - best_choice = &capital_word; - *rating_limit = upper_rating; - } - return new WERD_CHOICE(*best_choice); -} - - -/** - * @name choose_il1 - * - * Choose between the candidate il1 chars. - * @param first_char first choice - * @param second_char second choice - * @param third_char third choice - * @param prev_char prev in word - * @param next_char next in word - * @param next_next_char after next next in word - */ -const char* Dict::choose_il1(const char *first_char, - const char *second_char, - const char *third_char, - const char *prev_char, - const char *next_char, - const char *next_next_char) { - inT32 type1; //1/I/l type of first choice - inT32 type2; //1/I/l type of second choice - inT32 type3; //1/I/l type of third choice - - int first_char_length = strlen(first_char); - int prev_char_length = strlen(prev_char); - int next_char_length = strlen(next_char); - int next_next_char_length = strlen(next_next_char); - - if (*first_char == 'l' && *second_char != '\0') { - if (*second_char == 'I' - && (((prev_char_length != 0 && - getUnicharset().get_isupper (prev_char, prev_char_length)) && - (next_char_length == 0 || - !getUnicharset().get_islower (next_char, next_char_length)) && - (next_char_length == 0 || - !getUnicharset().get_isdigit (next_char, next_char_length))) || - ((next_char_length != 0 && - getUnicharset().get_isupper (next_char, next_char_length)) && - (prev_char_length == 0 || - !getUnicharset().get_islower (prev_char, prev_char_length)) && - (prev_char_length == 0 || - !getUnicharset().get_isdigit (prev_char, prev_char_length))))) - first_char = second_char; //override - else if (*second_char == '1' || *third_char == '1') { - if ((next_char_length != 0 && - getUnicharset().get_isdigit (next_char, next_char_length)) || - (prev_char_length != 0 && - getUnicharset().get_isdigit (prev_char, prev_char_length)) - || (*next_char == 'l' && - (next_next_char_length != 0 && - getUnicharset().get_isdigit (next_next_char, - next_next_char_length)))) { - first_char = "1"; - first_char_length = 1; - } - else if ((prev_char_length == 0 || - !getUnicharset().get_islower (prev_char, prev_char_length)) && - ((next_char_length == 0 || - !getUnicharset().get_islower (next_char, next_char_length)) || - (*next_char == 's' && - *next_next_char == 't'))) { - if (((*prev_char != '\'' && *prev_char != '`') || *next_char != '\0') - && ((*next_char != '\'' && *next_char != '`') - || *prev_char != '\0')) { - first_char = "1"; - first_char_length = 1; - } - } - } - if (*first_char == 'l' && *next_char != '\0' && - (prev_char_length == 0 || - !getUnicharset().get_isalpha (prev_char, prev_char_length))) { - type1 = 2; - - if (*second_char == '1') - type2 = 0; - else if (*second_char == 'I') - type2 = 1; - else if (*second_char == 'l') - type2 = 2; - else - type2 = type1; - - if (*third_char == '1') - type3 = 0; - else if (*third_char == 'I') - type3 = 1; - else if (*third_char == 'l') - type3 = 2; - else - type3 = type1; - -#if 0 - if (bigram_counts[*next_char][type2] > - bigram_counts[*next_char][type1]) { - first_char = second_char; - type1 = type2; - } - if (bigram_counts[*next_char][type3] > - bigram_counts[*next_char][type1]) { - first_char = third_char; - } -#endif - } - } - return first_char; -} - -/** - * @name fragment_state - * - * Given the current char choice and information about previously seen - * fragments, determines whether adjacent character fragments are - * present and whether they can be concatenated. - * - * The given prev_char_frag_info contains: - * - fragment: if not NULL contains information about immediately - * preceeding fragmented character choice - * - num_fragments: number of fragments that have been used so far - * to construct a character - * - certainty: certainty of the current choice or minimum - * certainty of all fragments concatenated so far - * - rating: rating of the current choice or sum of fragment - * ratings concatenated so far - * - * The output char_frag_info is filled in as follows: - * - character: is set to be NULL if the choice is a non-matching - * or non-ending fragment piece; is set to unichar of the given choice - * if it represents a regular character or a matching ending fragment - * - fragment,num_fragments,certainty,rating are set as described above - * - * @returns false if a non-matching fragment is discovered, true otherwise. - */ -bool Dict::fragment_state_okay(UNICHAR_ID curr_unichar_id, - float curr_rating, float curr_certainty, - const CHAR_FRAGMENT_INFO *prev_char_frag_info, - const char *debug, int word_ending, - CHAR_FRAGMENT_INFO *char_frag_info) { - const CHAR_FRAGMENT *this_fragment = - getUnicharset().get_fragment(curr_unichar_id); - const CHAR_FRAGMENT *prev_fragment = - prev_char_frag_info != NULL ? prev_char_frag_info->fragment : NULL; - - // Print debug info for fragments. - if (debug && (prev_fragment || this_fragment)) { - cprintf("%s check fragments: choice=%s word_ending=%d\n", debug, - getUnicharset().debug_str(curr_unichar_id).string(), - word_ending); - if (prev_fragment) { - cprintf("prev_fragment %s\n", prev_fragment->to_string().string()); - } - if (this_fragment) { - cprintf("this_fragment %s\n", this_fragment->to_string().string()); - } - } - - char_frag_info->unichar_id = curr_unichar_id; - char_frag_info->fragment = this_fragment; - char_frag_info->rating = curr_rating; - char_frag_info->certainty = curr_certainty; - char_frag_info->num_fragments = 1; - if (prev_fragment && !this_fragment) { - if (debug) tprintf("Skip choice with incomplete fragment\n"); - return false; - } - if (this_fragment) { - // We are dealing with a fragment. - char_frag_info->unichar_id = INVALID_UNICHAR_ID; - if (prev_fragment) { - if (!this_fragment->is_continuation_of(prev_fragment)) { - if (debug) tprintf("Non-matching fragment piece\n"); - return false; - } - if (this_fragment->is_ending()) { - char_frag_info->unichar_id = - getUnicharset().unichar_to_id(this_fragment->get_unichar()); - char_frag_info->fragment = NULL; - if (debug) { - tprintf("Built character %s from fragments\n", - getUnicharset().debug_str( - char_frag_info->unichar_id).string()); - } - } else { - if (debug) tprintf("Record fragment continuation\n"); - char_frag_info->fragment = this_fragment; - } - // Update certainty and rating. - char_frag_info->rating = - prev_char_frag_info->rating + curr_rating; - char_frag_info->num_fragments = prev_char_frag_info->num_fragments + 1; - char_frag_info->certainty = - MIN(curr_certainty, prev_char_frag_info->certainty); - } else { - if (this_fragment->is_beginning()) { - if (debug) cprintf("Record fragment beginning\n"); - } else { - if (debug) { - tprintf("Non-starting fragment piece with no prev_fragment\n"); - } - return false; - } - } - } - if (word_ending && char_frag_info->fragment) { - if (debug) tprintf("Word can not end with a fragment\n"); - return false; - } - return true; -} -/** - * top_fragments_permute_and_select - * - * Creates a copy of character choices list that contain only fragments - * and the best non-fragmented character choice. - * Permutes character in this shortened list, builds characters from - * fragments if possible and returns a better choice if found. - */ -WERD_CHOICE *Dict::top_fragments_permute_and_select( - const BLOB_CHOICE_LIST_VECTOR &char_choices, - float rating_limit) { - if (char_choices.length() <= 1 || - char_choices.length() > MAX_PERM_LENGTH) { - return NULL; - } - // See it would be possible to benefit from permuting fragments. - int x; - float min_rating = 0.0; - BLOB_CHOICE_IT blob_choice_it; - for (x = 0; x < char_choices.length(); ++x) { - blob_choice_it.set_to_list(char_choices.get(x)); - if (blob_choice_it.data()) { - min_rating += blob_choice_it.data()->rating(); - } - if (min_rating >= rating_limit) { - return NULL; - } - } - if (fragments_debug > 1) { - tprintf("A choice with fragment beats top choice\n"); - tprintf("Running fragment permuter...\n"); - } - - // Construct a modified choices list that contains (for each position): - // the best choice, all fragments and at least one choice for - // a non-fragmented character. - BLOB_CHOICE_LIST_VECTOR frag_char_choices(char_choices.length()); - for (x = 0; x < char_choices.length(); ++x) { - bool need_nonfrag_char = true; - BLOB_CHOICE_LIST *frag_choices = new BLOB_CHOICE_LIST(); - BLOB_CHOICE_IT frag_choices_it; - frag_choices_it.set_to_list(frag_choices); - blob_choice_it.set_to_list(char_choices.get(x)); - for (blob_choice_it.mark_cycle_pt(); !blob_choice_it.cycled_list(); - blob_choice_it.forward()) { - if (getUnicharset().get_fragment(blob_choice_it.data()->unichar_id())) { - frag_choices_it.add_after_then_move( - new BLOB_CHOICE(*(blob_choice_it.data()))); - } else if (need_nonfrag_char) { - frag_choices_it.add_after_then_move( - new BLOB_CHOICE(*(blob_choice_it.data()))); - need_nonfrag_char = false; - } - } - frag_char_choices += frag_choices; - } - - WERD_CHOICE *best_choice = new WERD_CHOICE(&getUnicharset()); - best_choice->make_bad(); - WERD_CHOICE word(&getUnicharset(), MAX_PERM_LENGTH); - word.set_permuter(TOP_CHOICE_PERM); - float certainties[MAX_PERM_LENGTH]; - this->go_deeper_fxn_ = &tesseract::Dict::go_deeper_top_fragments_fxn; - int attempts_left = max_permuter_attempts; - permute_choices((fragments_debug > 1) ? "fragments_debug" : NULL, - frag_char_choices, 0, NULL, &word, certainties, - &rating_limit, best_choice, &attempts_left, NULL); - - frag_char_choices.delete_data_pointers(); - return best_choice; -} - -/** - * permute_choices - * - * Call append_choices() for each BLOB_CHOICE in BLOB_CHOICE_LIST - * with the given char_choice_index in char_choices. - */ -void Dict::permute_choices( - const char *debug, - const BLOB_CHOICE_LIST_VECTOR &char_choices, - int char_choice_index, - const CHAR_FRAGMENT_INFO *prev_char_frag_info, - WERD_CHOICE *word, - float certainties[], - float *limit, - WERD_CHOICE *best_choice, - int *attempts_left, - void *more_args) { - if (debug) { - tprintf("%s permute_choices: char_choice_index=%d" - " limit=%g rating=%g, certainty=%g word=%s\n", - debug, char_choice_index, *limit, word->rating(), - word->certainty(), word->debug_string().string()); - } - if (char_choice_index < char_choices.length()) { - BLOB_CHOICE_IT blob_choice_it; - blob_choice_it.set_to_list(char_choices.get(char_choice_index)); - for (blob_choice_it.mark_cycle_pt(); !blob_choice_it.cycled_list(); - blob_choice_it.forward()) { - (*attempts_left)--; - append_choices(debug, char_choices, *(blob_choice_it.data()), - char_choice_index, prev_char_frag_info, word, - certainties, limit, best_choice, attempts_left, more_args); - if (*attempts_left <= 0) { - if (debug) tprintf("permute_choices(): attempts_left is 0\n"); - break; - } - } - } -} - -/** - * append_choices - * - * Checks to see whether or not the next choice is worth appending to - * the word being generated. If so then keeps going deeper into the word. - * - * This function assumes that Dict::go_deeper_fxn_ is set. - */ -void Dict::append_choices( - const char *debug, - const BLOB_CHOICE_LIST_VECTOR &char_choices, - const BLOB_CHOICE &blob_choice, - int char_choice_index, - const CHAR_FRAGMENT_INFO *prev_char_frag_info, - WERD_CHOICE *word, - float certainties[], - float *limit, - WERD_CHOICE *best_choice, - int *attempts_left, - void *more_args) { - int word_ending = - (char_choice_index == char_choices.length() - 1) ? true : false; - - // Deal with fragments. - CHAR_FRAGMENT_INFO char_frag_info; - if (!fragment_state_okay(blob_choice.unichar_id(), blob_choice.rating(), - blob_choice.certainty(), prev_char_frag_info, debug, - word_ending, &char_frag_info)) { - return; // blob_choice must be an invalid fragment - } - // Search the next letter if this character is a fragment. - if (char_frag_info.unichar_id == INVALID_UNICHAR_ID) { - permute_choices(debug, char_choices, char_choice_index + 1, - &char_frag_info, word, certainties, limit, - best_choice, attempts_left, more_args); - return; - } - - // Add the next unichar. - float old_rating = word->rating(); - float old_certainty = word->certainty(); - uinT8 old_permuter = word->permuter(); - certainties[word->length()] = char_frag_info.certainty; - word->append_unichar_id_space_allocated( - char_frag_info.unichar_id, char_frag_info.num_fragments, - char_frag_info.rating, char_frag_info.certainty); - - // Explore the next unichar. - (this->*go_deeper_fxn_)(debug, char_choices, char_choice_index, - &char_frag_info, word_ending, word, certainties, - limit, best_choice, attempts_left, more_args); - - // Remove the unichar we added to explore other choices in it's place. - word->remove_last_unichar_id(); - word->set_rating(old_rating); - word->set_certainty(old_certainty); - word->set_permuter(old_permuter); -} - -/** - * go_deeper_top_fragments_fxn - * - * While the choice being composed so far could be better - * than best_choice keeps exploring char_choices. - * If the end of the word is reached and the word is better than - * best_choice, copies word to best_choice and logs the new word choice. - */ -void Dict::go_deeper_top_fragments_fxn( - const char *debug, const BLOB_CHOICE_LIST_VECTOR &char_choices, - int char_choice_index, const CHAR_FRAGMENT_INFO *prev_char_frag_info, - bool word_ending, WERD_CHOICE *word, float certainties[], float *limit, - WERD_CHOICE *best_choice, int *attempts_left, void *more_args) { - if (word->rating() < *limit) { - if (word_ending) { - if (fragments_debug > 1) { - tprintf("fragments_debug new choice = %s\n", - word->debug_string().string()); - } - *limit = word->rating(); - adjust_non_word(word, certainties, &char_choices, permute_debug); - update_best_choice(*word, best_choice); - } else { // search the next letter - permute_choices(debug, char_choices, char_choice_index + 1, - prev_char_frag_info, word, certainties, limit, - best_choice, attempts_left, more_args); - } - } else { - if (fragments_debug > 1) { - tprintf("fragments_debug pruned word (%s, rating=%4.2f, limit=%4.2f)\n", - word->debug_string().string(), word->rating(), *limit); - } - } -} - -} // namespace tesseract diff --git a/dict/permute.h b/dict/permute.h deleted file mode 100644 index f7ff6cad95..0000000000 --- a/dict/permute.h +++ /dev/null @@ -1,150 +0,0 @@ -/* -*-C-*- - ******************************************************************************** - * - * File: permute.h (Formerly permute.h) - * Description: Permute choices together - * Author: Mark Seaman, OCR Technology - * Created: Fri Sep 22 14:05:51 1989 - * Modified: Mon May 20 16:32:04 1991 (Mark Seaman) marks@hpgrlt - * Language: C - * Package: N/A - * Status: Experimental (Do Not Distribute) - * - * (c) Copyright 1989, Hewlett-Packard Company. - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** http://www.apache.org/licenses/LICENSE-2.0 - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - * - ********************************************************************************/ -#ifndef PERMUTE_H -#define PERMUTE_H - -/*---------------------------------------------------------------------- - I n c l u d e s -----------------------------------------------------------------------*/ - -#include "ratngs.h" -#include "params.h" -#include "unicharset.h" - -#define MAX_PERM_LENGTH 128 - -/*---------------------------------------------------------------------- - V a r i a b l e s -----------------------------------------------------------------------*/ -extern INT_VAR_H(fragments_debug, 0, "Debug character fragments"); -extern INT_VAR_H(segment_debug, 0, "Debug the whole segmentation process"); -extern BOOL_VAR_H(permute_debug, 0, "char permutation debug"); - -extern BOOL_VAR_H(permute_script_word, 0, - "Turn on word script consistency permuter"); - -extern BOOL_VAR_H(permute_fixed_length_dawg, 0, - "Turn on fixed-length phrasebook search permuter"); - -extern BOOL_VAR_H(segment_segcost_rating, 0, - "incorporate segmentation cost in word rating?"); - -extern double_VAR_H(segment_reward_script, 0.95, - "Score multipler for script consistency within a word. " - "Being a 'reward' factor, it should be <= 1. " - "Smaller value implies bigger reward."); - -extern BOOL_VAR_H(permute_chartype_word, 0, - "Turn on character type (property) consistency permuter"); -extern double_VAR_H(segment_reward_chartype, 0.97, - "Score multipler for char type consistency within a word. "); - -extern double_VAR_H(segment_reward_ngram_best_choice, 0.99, - "Score multipler for ngram permuter's best choice" - " (only used in the Han script path)."); - -extern INT_VAR_H(max_permuter_attempts, 100000, - "Maximum number of different character choices to consider" - " during permutation. This limit is especially useful when" - " user patterns are specified, since overly generic patterns" - " can result in dawg search exploring an overly large number" - "of options."); - -extern int permute_only_top; - -/*---------------------------------------------------------------------- - F u n c t i o n s -----------------------------------------------------------------------*/ -void adjust_non_word(const char *word, const char *word_lengths, - float rating, float *new_rating, float *adjust_factor); - -const char* choose_il1(const char *first_char, //first choice - const char *second_char, //second choice - const char *third_char, //third choice - const char *prev_char, //prev in word - const char *next_char, //next in word - const char *next_next_char); - -namespace tesseract { - -// This is an awkward solution to allow "compounding" of permuter effects. -// Right now, each permuter generates a WERD_CHOICE with some modified -// rating which is compared to the current best choice, and the winner -// is saved. Therefore, independent permuter improvements, eg. from script -// consistency, dictionary check, and punctuation promoting, override each -// other and can not be combined. -// We need a trellis and someway to modify the path cost. Instead, we -// approximate by saving a permutation string, which records the preferred -// char choice [0-9] at each position [0..#chunks], and a cumulative reward -// factor. Non-conflicting changes can be accumulated and the combined -// result will be returned. -// Default_bias is the initial value for the base multiplier. In other words, -// it is the multiplier for raw choice rating if nothing is modified. -// This would be 1.0 when used with reward-based permuters in CJK-path, -// but it could be > 1 (eg. segment_penalty_garbage) to be compatible with -// penalty-based permuters in the Latin path. -// Note this class does not handle fragmented characters. It does so by -// setting the preferred position of fragmented characters to '1' at Init, -// which effectively skips the fragment choice. However, it can still be -// overridden if collision is allowed. It is the responsibility of the -// permuters to avoid permuting fragmented characters. -class PermuterState { - public: - PermuterState(); - - void Init(const BLOB_CHOICE_LIST_VECTOR& char_choices, - const UNICHARSET &unicharset, - float default_bias, - bool debug); - - void AddPreference(int start_pos, char* pos_str, float weight); - - void AddPreference(int char_pos, BLOB_CHOICE* blob_choice, float weight); - - WERD_CHOICE* GetPermutedWord(float *certainties, float *adjust_factor); - - void set_allow_collision(bool flag) { allow_collision_ = flag; } - void set_adjust_factor(float factor) { adjust_factor_ = factor; } - void set_debug(bool debug) { debug_ = debug; } - bool position_marked(int pos) { return perm_state_[pos] != kPosFree; } - - private: - static const char kPosFree = '.'; - - const UNICHARSET *unicharset_; - - const BLOB_CHOICE_LIST_VECTOR *char_choices_; // reference pointer only - // does not need to be allocated or freed - char perm_state_[MAX_PERM_LENGTH]; // handles upto MAX_PERM_LENGTH-1 states - // stores preferred char choices, '0'..'9', or '.' - int word_length_; // the number of char positions in the word - bool allow_collision_; // can previously set preference to be overwritten? - float adjust_factor_; // multiplying factor for rating adjustment - bool debug_; // whether debug statements should be printed -}; - -} // namespace tesseract - -#endif diff --git a/dict/states.cpp b/dict/states.cpp deleted file mode 100644 index 0a5393f80c..0000000000 --- a/dict/states.cpp +++ /dev/null @@ -1,274 +0,0 @@ -/* -*-C-*- - ******************************************************************************** - * - * File: states.c (Formerly states.c) - * Description: Representations of search states - * Author: Mark Seaman, OCR Technology - * Created: Wed May 16 15:49:34 1990 - * Modified: Mon Jun 17 17:54:41 1991 (Mark Seaman) marks@hpgrlt - * Language: C - * Package: N/A - * Status: Experimental (Do Not Distribute) - * - * (c) Copyright 1990, Hewlett-Packard Company. - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** http://www.apache.org/licenses/LICENSE-2.0 - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - * - *********************************************************************************/ -/*---------------------------------------------------------------------- - I n c l u d e s -----------------------------------------------------------------------*/ -#include "states.h" -#include "structures.h" -#include "callcpp.h" - -/*------------------------------------------------------------------------- - Variables ---------------------------------------------------------------------------*/ -makestructure(newstate, free_state, STATE); - -/*---------------------------------------------------------------------- - F u n c t i o n s -----------------------------------------------------------------------*/ -/** - * @name bin_to_chunks - * - * Convert a representation of the search state in "STATE" form to one - * in "SEARCH_STATE" form. Create the memory required to hold the - * resultant state value. - * - * @param state The state to convert - */ -SEARCH_STATE bin_to_chunks(STATE *state, int num_joints) { - int x; - unsigned int mask; - int depth; - int pieces = 0; - SEARCH_STATE s; - - s = memalloc (sizeof (int) * (ones_in_state (state, num_joints) + 1)); - - depth = 1; - mask = 1 << (num_joints - 1 - 32); - for (x = num_joints; x > 32; x--) { - if (state->part1 & mask) { - s[depth++] = pieces; - pieces = 0; - } - else { - pieces++; - } - mask >>= 1; - } - - if (num_joints > 32) - mask = 1 << 31; - else - mask = 1 << (num_joints - 1); - - while (x--) { - if (state->part2 & mask) { - s[depth++] = pieces; - pieces = 0; - } - else { - pieces++; - } - mask >>= 1; - } - s[0] = depth - 1; - - return (s); -} - - -/** - * bin_to_pieces - * - * Convert the binary (bit vector) format of a search state to an array - * of piece counts. This array has a zero element after the last valid - * character. - */ -void bin_to_pieces(STATE *state, int num_joints, PIECES_STATE pieces) { - int x; - unsigned int mask; /* Bit mask */ - inT16 num_pieces = 0; - /* Preset mask */ - mask = ((num_joints > 32) ? - (1 << (num_joints - 1 - 32)) : (1 << (num_joints - 1))); - - pieces[num_pieces] = 0; - - for (x = num_joints - 1; x >= 0; x--) { - /* Iterate all bits */ - pieces[num_pieces]++; - - if ((x < 32) ? /* Test for 1 bit */ - ((state->part2 & mask) ? TRUE : FALSE) : - ((state->part1 & mask) ? TRUE : FALSE)) { - pieces[++num_pieces] = 0; - } - /* Next mask value */ - mask = ((mask == 1) ? (1 << 31) : (mask >> 1)); - } - pieces[num_pieces]++; - pieces[++num_pieces] = 0; - ASSERT_HOST (num_pieces < MAX_NUM_CHUNKS + 2); -} - - -/** - * insert_new_chunk - * - * Add a new chunk division into this state vector at the location - * requested. - */ -void insert_new_chunk(register STATE *state, - register int index, - register int num_joints) { - register unsigned int mask; - register unsigned int result; - - index = (num_joints - index); - if (index < 32) { - mask = ~0; - mask <<= index; - result = (mask & state->part2) << 1; - result |= (~mask & state->part2); - state->part1 <<= 1; - if (state->part2 & 0x80000000) - state->part1 |= 1; - state->part2 = result; - } - else { - mask = ~0; - mask <<= index - 32; - result = (mask & state->part1) << 1; - result |= (~mask & state->part1); - state->part1 = result; - } -} - - -/** - * new_state - * - * Create a memory space for a new state variable. Set its initial - * value according to the parameters. - */ -STATE *new_state(STATE *oldstate) { - STATE *this_state; - - this_state = newstate (); - this_state->part1 = oldstate->part1; - this_state->part2 = oldstate->part2; - return (this_state); -} - - -/** - * ones_in_state - * - * Return the number of ones that are in this state. - */ -int ones_in_state(STATE *state, int num_joints) { - inT8 num_ones = 0; - inT8 x; - unsigned int mask; - - if (num_joints > 32) /* Preset mask */ - mask = 1 << (num_joints - 1 - 32); - else - mask = 1 << (num_joints - 1); - - for (x = num_joints - 1; x >= 0; x--) { - /* Iterate all bits */ - - if (x < 32) - num_ones += ((state->part2 & mask) ? 1 : 0); - else - num_ones += ((state->part1 & mask) ? 1 : 0); - - if (mask == 1) /* Next mask value */ - mask = 1 << 31; - else - mask >>= 1; - } - - return (num_ones); -} - - -/** - * print_state - * - * Print out the current state variable on a line with a label. - */ -void print_state(const char *label, STATE *state, int num_joints) { - int x; - unsigned int mask; /* Bit mask */ - - if (num_joints > 32) /* Preset mask */ - mask = 1 << (num_joints - 1 - 32); - else - mask = 1 << (num_joints - 1); - - cprintf ("%s ", label); - - for (x = num_joints - 1; x >= 0; x--) { - /* Iterate all bits */ - - if (x < 32) - cprintf ("%d", ((state->part2 & mask) ? 1 : 0)); - else - cprintf ("%d", ((state->part1 & mask) ? 1 : 0)); - if (x % 4 == 0) - cprintf (" "); - - if (mask == 1) /* Next mask value */ - mask = 1 << 31; - else - mask >>= 1; - } - - new_line(); -} - -// Prints out the number of fragments in each segment in a state to -// toappend. -void print_state(STATE *state, int num_joints, STRING *toappend) { - PIECES_STATE pieces; - bin_to_pieces(state, num_joints, pieces); - for (int i = 0; pieces[i] > 0; i++) { - if (i > 0) { - toappend->add_str_int(" ", pieces[i]); - } else { - toappend->add_str_int("", pieces[i]); - } - } -} - -/** - * set_n_ones - * - * Set the first n bits in a state. - */ -void set_n_ones(STATE *state, int n) { - if (n < 32) { - state->part2 = ~0; - state->part2 >>= 32 - n; - state->part1 = 0; - } - else { - state->part2 = ~0; - state->part1 = ~0; - state->part1 >>= 64 - n; - } -} diff --git a/dict/states.h b/dict/states.h deleted file mode 100644 index ef0640171e..0000000000 --- a/dict/states.h +++ /dev/null @@ -1,74 +0,0 @@ -/* -*-C-*- - ******************************************************************************** - * - * File: states.h (Formerly states.h) - * Description: Representations of search states - * Author: Mark Seaman, OCR Technology - * Created: Wed May 16 15:52:40 1990 - * Modified: Tue May 21 16:26:21 1991 (Mark Seaman) marks@hpgrlt - * Language: C - * Package: N/A - * Status: Experimental (Do Not Distribute) - * - * (c) Copyright 1990, Hewlett-Packard Company. - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** http://www.apache.org/licenses/LICENSE-2.0 - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - * - *********************************************************************************/ -#ifndef STATES_H -#define STATES_H - -/*---------------------------------------------------------------------- - I n c l u d e s -----------------------------------------------------------------------*/ -#include "host.h" -#include "strngs.h" - -/*---------------------------------------------------------------------- - T y p e s -----------------------------------------------------------------------*/ -#define MAX_NUM_CHUNKS 64 /* Limit on pieces */ - -typedef struct -{ - uinT32 part1; - uinT32 part2; -} STATE; - -/** State variable for search */ -typedef int *SEARCH_STATE; - -/** State variable for search */ -typedef uinT8 PIECES_STATE[MAX_NUM_CHUNKS + 2]; - -/*---------------------------------------------------------------------- - F u n c t i o n s -----------------------------------------------------------------------*/ -SEARCH_STATE bin_to_chunks(STATE *state, int num_joints); - -void bin_to_pieces(STATE *state, int num_joints, PIECES_STATE pieces); - -void insert_new_chunk(register STATE *state, - register int index, - int num_joints); - -STATE *new_state(STATE *oldstate); - -int ones_in_state(STATE *state, int num_joints); - -void print_state(const char *label, STATE *state, int num_joints); - -void print_state(STATE *state, int num_joints, STRING *toappend); - -void set_n_ones(STATE *state, int n); - -extern void free_state(STATE *); - -#endif diff --git a/dict/stopper.cpp b/dict/stopper.cpp index ddf8261437..409f073649 100644 --- a/dict/stopper.cpp +++ b/dict/stopper.cpp @@ -16,215 +16,72 @@ ** limitations under the License. ******************************************************************************/ +#include +#include +#include +#include + #include "stopper.h" -#include "matchdefs.h" -#include "callcpp.h" -#include "permute.h" -#include "danerror.h" +#include "ambigs.h" +#include "ccutil.h" #include "const.h" -#include "efio.h" -#include "scanutils.h" -#include "unichar.h" -#include "params.h" +#include "danerror.h" #include "dict.h" +#include "efio.h" +#include "helpers.h" #include "image.h" -#include "ccutil.h" +#include "matchdefs.h" +#include "pageres.h" +#include "params.h" #include "ratngs.h" -#include "ambigs.h" - -#include -#include -#include -#include -#ifdef __UNIX__ -#include -#endif +#include "scanutils.h" +#include "unichar.h" #ifdef _MSC_VER #pragma warning(disable:4244) // Conversion warnings #pragma warning(disable:4800) // int/bool warnings #endif -/* these are kludges - add appropriate .h file later */ -/* from adaptmatch.cpp */ -#define MAX_WERD_SIZE 100 - -typedef struct -{ - VIABLE_CHOICE Choice; - float ChunkCertainty[MAX_NUM_CHUNKS]; - UNICHAR_ID ChunkClass[MAX_NUM_CHUNKS]; -} EXPANDED_CHOICE; - -void DeleteViableChoiceStruct(void *vcs) { - delete (static_cast(vcs)); -} - -#define BestCertainty(Choices) \ - (((VIABLE_CHOICE) first_node (Choices))->Certainty) - -#define BestRating(Choices) (((VIABLE_CHOICE) first_node (Choices))->Rating) - -#define BestFactor(Choices) \ - (((VIABLE_CHOICE) first_node (Choices))->AdjustFactor) - +using tesseract::ScriptPos; /**---------------------------------------------------------------------------- Private Code ----------------------------------------------------------------------------**/ -// Returns -1 if the rating for Choice1 is less than the rating for Choice2, -// otherwise returns 1. -static int CmpChoiceRatings(void *arg1, // VIABLE_CHOICE Choice1 - void *arg2) { // VIABLE_CHOICE Choice2 - float R1, R2; - VIABLE_CHOICE Choice1 = (VIABLE_CHOICE) arg1; - VIABLE_CHOICE Choice2 = (VIABLE_CHOICE) arg2; - R1 = Choice1->Rating; - R2 = Choice2->Rating; - return (R1 < R2) ? -1 : 1; -} - -// Expands Choice and places the results in ExpandedChoice. The primary -// function of expansion is to create an two arrays, one which holds the -// corresponding certainty for each chunk in Choice, and one which holds -// the class for each chunk. -static void ExpandChoice(VIABLE_CHOICE Choice, - EXPANDED_CHOICE *ExpandedChoice) { - int i, j, Chunk; - ExpandedChoice->Choice = Choice; - for (i = 0, Chunk = 0; i < Choice->Length; i++) - for (j = 0; j < Choice->Blob[i].NumChunks; j++, Chunk++) { - ExpandedChoice->ChunkCertainty[Chunk] = Choice->Blob[i].Certainty; - ExpandedChoice->ChunkClass[Chunk] = Choice->Blob[i].Class; - } -} - -VIABLE_CHOICE_STRUCT::VIABLE_CHOICE_STRUCT(int length) - : Length(length) { - Blob = new CHAR_CHOICE[length]; - blob_choices = NULL; -} - -VIABLE_CHOICE_STRUCT::VIABLE_CHOICE_STRUCT() : Length(0) { - Blob = NULL; - blob_choices = NULL; -} - -VIABLE_CHOICE_STRUCT::~VIABLE_CHOICE_STRUCT() { - delete []Blob; - if (blob_choices) { - blob_choices->deep_clear(); - delete blob_choices; - } -} - -void VIABLE_CHOICE_STRUCT::Init( - const WERD_CHOICE &word_choice, - const PIECES_STATE &pieces_state, - const float certainties[], - FLOAT32 adjust_factor) { - this->Rating = word_choice.rating(); - this->Certainty = word_choice.certainty(); - this->AdjustFactor = adjust_factor; - this->ComposedFromCharFragments = false; - ASSERT_HOST(this->Length == word_choice.length()); - - for (int i = 0, bw_idx = 0; i < word_choice.length(); i++, bw_idx++) { - int blob_width = pieces_state[bw_idx]; - CHAR_CHOICE *blob_choice = &this->Blob[i]; - blob_choice->Class = word_choice.unichar_id(i); - blob_choice->NumChunks = blob_width; - blob_choice->Certainty = certainties[i]; - for (int f = 1; f < word_choice.fragment_length(i); ++f) { - blob_width = pieces_state[++bw_idx]; - assert(blob_width > 0); - blob_choice->NumChunks += blob_width; - this->ComposedFromCharFragments = true; - } - } -} - -void VIABLE_CHOICE_STRUCT::SetBlobChoices( - const BLOB_CHOICE_LIST_VECTOR &src_choices) { - if (blob_choices != NULL) { - blob_choices->deep_clear(); - } else { - blob_choices = new BLOB_CHOICE_LIST_CLIST(); - } - BLOB_CHOICE_LIST_C_IT list_it(blob_choices); - - for (int i = 0; i < src_choices.size(); ++i) { - BLOB_CHOICE_LIST *cc_list = new BLOB_CHOICE_LIST(); - cc_list->deep_copy(src_choices[i], &BLOB_CHOICE::deep_copy); - list_it.add_after_then_move(cc_list); - } -} namespace tesseract { -// If the certainty of any chunk in Choice (item1) is not ambiguous with the -// corresponding chunk in the best choice (item2), frees Choice and -// returns true. -int Dict::FreeBadChoice( - void *item1, // VIABLE_CHOICE Choice, - void *item2) { // EXPANDED_CHOICE *BestChoice - int i, j, Chunk; - FLOAT32 Threshold; - VIABLE_CHOICE Choice = reinterpret_cast(item1); - EXPANDED_CHOICE *BestChoice = reinterpret_cast(item2); - Threshold = StopperAmbigThreshold(BestChoice->Choice->AdjustFactor, - Choice->AdjustFactor); - for (i = 0, Chunk = 0; i < Choice->Length; i++) { - for (j = 0; j < Choice->Blob[i].NumChunks; j++, Chunk++) { - if (Choice->Blob[i].Class != BestChoice->ChunkClass[Chunk] && - Choice->Blob[i].Certainty - BestChoice->ChunkCertainty[Chunk] < - Threshold) { - if (stopper_debug_level >= 2) - PrintViableChoice(stderr, "\nDiscarding bad choice: ", Choice); - delete Choice; - return true; - } - } - } - return false; -} - -bool Dict::AcceptableChoice(BLOB_CHOICE_LIST_VECTOR *Choices, - WERD_CHOICE *BestChoice, - DANGERR *fixpt, - ACCEPTABLE_CHOICE_CALLER caller, - bool *modified_blobs) { +bool Dict::AcceptableChoice(const WERD_CHOICE& best_choice, + XHeightConsistencyEnum xheight_consistency) { float CertaintyThreshold = stopper_nondict_certainty_base; int WordSize; - if (modified_blobs != NULL) *modified_blobs = false; if (stopper_no_acceptable_choices) return false; - if (fixpt != NULL) fixpt->clear(); - if (BestChoice->length() == 0) - return false; - if (caller == CHOPPER_CALLER && BestChoice->fragment_mark()) { - if (stopper_debug_level >= 1) { - cprintf("AcceptableChoice(): a choice with fragments beats BestChoice"); - } - return false; - } + if (best_choice.length() == 0) return false; - bool no_dang_ambigs = (GetMaxFixedLengthDawgIndex() >= 0 || - NoDangerousAmbig(BestChoice, fixpt, true, - Choices, modified_blobs)); - bool is_valid_word = valid_word_permuter(BestChoice->permuter(), false); - bool is_case_ok = case_ok(*BestChoice, getUnicharset()); + bool no_dang_ambigs = !best_choice.dangerous_ambig_found(); + bool is_valid_word = valid_word_permuter(best_choice.permuter(), false); + bool is_case_ok = case_ok(best_choice, getUnicharset()); - if (stopper_debug_level >= 1) - tprintf("\nStopper: %s (word=%c, case=%c)\n", - BestChoice->debug_string().string(), + if (stopper_debug_level >= 1) { + const char *xht = "UNKNOWN"; + switch (xheight_consistency) { + case XH_GOOD: xht = "NORMAL"; break; + case XH_SUBNORMAL: xht = "SUBNORMAL"; break; + case XH_INCONSISTENT: xht = "INCONSISTENT"; break; + default: xht = "UNKNOWN"; + } + tprintf("\nStopper: %s (word=%c, case=%c, xht_ok=%s=[%g,%g])\n", + best_choice.unichar_string().string(), (is_valid_word ? 'y' : 'n'), - (is_case_ok ? 'y' : 'n')); - + (is_case_ok ? 'y' : 'n'), + xht, + best_choice.min_x_height(), + best_choice.max_x_height()); + } // Do not accept invalid words in PASS1. if (reject_offset_ <= 0.0f && !is_valid_word) return false; if (is_valid_word && is_case_ok) { - WordSize = LengthOfShortestAlphaRun(*BestChoice); + WordSize = LengthOfShortestAlphaRun(best_choice); WordSize -= stopper_smallword_size; if (WordSize < 0) WordSize = 0; @@ -232,47 +89,45 @@ bool Dict::AcceptableChoice(BLOB_CHOICE_LIST_VECTOR *Choices, } if (stopper_debug_level >= 1) - tprintf("Stopper: Certainty = %4.1f, Threshold = %4.1f\n", - BestChoice->certainty(), CertaintyThreshold); + tprintf("Stopper: Rating = %4.1f, Certainty = %4.1f, Threshold = %4.1f\n", + best_choice.rating(), best_choice.certainty(), CertaintyThreshold); if (no_dang_ambigs && - BestChoice->certainty() > CertaintyThreshold && - UniformCertainties(*Choices, *BestChoice)) { + best_choice.certainty() > CertaintyThreshold && + xheight_consistency < XH_INCONSISTENT && + UniformCertainties(best_choice)) { return true; } else { - if (stopper_debug_level >= 2) { + if (stopper_debug_level >= 1) { tprintf("AcceptableChoice() returned false" - " (no_dang_ambig:%d cert:%g thresh:%g uniform:%d)\n", - no_dang_ambigs, BestChoice->certainty(), + " (no_dang_ambig:%d cert:%.4g thresh:%g uniform:%d)\n", + no_dang_ambigs, best_choice.certainty(), CertaintyThreshold, - UniformCertainties(*Choices, *BestChoice)); + UniformCertainties(best_choice)); } return false; } } -bool Dict::AcceptableResult(const WERD_CHOICE &BestChoice) { +bool Dict::AcceptableResult(WERD_RES* word) { + if (word->best_choice == NULL) return false; float CertaintyThreshold = stopper_nondict_certainty_base - reject_offset_; int WordSize; if (stopper_debug_level >= 1) { - tprintf("\nRejecter: %s (word=%c, case=%c, unambig=%c)\n", - BestChoice.debug_string().string(), - (valid_word(BestChoice) ? 'y' : 'n'), - (case_ok(BestChoice, getUnicharset()) ? 'y' : 'n'), - ((list_rest (best_choices_) != NIL_LIST) ? 'n' : 'y')); + tprintf("\nRejecter: %s (word=%c, case=%c, unambig=%c, multiple=%c)\n", + word->best_choice->debug_string().string(), + (valid_word(*word->best_choice) ? 'y' : 'n'), + (case_ok(*word->best_choice, getUnicharset()) ? 'y' : 'n'), + word->best_choice->dangerous_ambig_found() ? 'n' : 'y', + word->best_choices.singleton() ? 'n' : 'y'); } - if (BestChoice.length() == 0 || CurrentWordAmbig()) - return false; - if (BestChoice.fragment_mark()) { - if (stopper_debug_level >= 1) { - cprintf("AcceptableResult(): a choice with fragments beats BestChoice\n"); - } + if (word->best_choice->length() == 0 || !word->best_choices.singleton()) return false; - } - if (valid_word(BestChoice) && case_ok(BestChoice, getUnicharset())) { - WordSize = LengthOfShortestAlphaRun(BestChoice); + if (valid_word(*word->best_choice) && + case_ok(*word->best_choice, getUnicharset())) { + WordSize = LengthOfShortestAlphaRun(*word->best_choice); WordSize -= stopper_smallword_size; if (WordSize < 0) WordSize = 0; @@ -280,309 +135,25 @@ bool Dict::AcceptableResult(const WERD_CHOICE &BestChoice) { } if (stopper_debug_level >= 1) - cprintf ("Rejecter: Certainty = %4.1f, Threshold = %4.1f ", - BestChoice.certainty(), CertaintyThreshold); + tprintf("Rejecter: Certainty = %4.1f, Threshold = %4.1f ", + word->best_choice->certainty(), CertaintyThreshold); - if (BestChoice.certainty() > CertaintyThreshold && + if (word->best_choice->certainty() > CertaintyThreshold && !stopper_no_acceptable_choices) { if (stopper_debug_level >= 1) - cprintf("ACCEPTED\n"); + tprintf("ACCEPTED\n"); return true; - } - else { + } else { if (stopper_debug_level >= 1) - cprintf("REJECTED\n"); + tprintf("REJECTED\n"); return false; } } -bool Dict::AlternativeChoicesWorseThan(FLOAT32 Threshold) { - LIST Alternatives; - VIABLE_CHOICE Choice; - Alternatives = list_rest (best_choices_); - iterate(Alternatives) { - Choice = (VIABLE_CHOICE) first_node (Alternatives); - if (Choice->AdjustFactor <= Threshold) - return false; - } - return true; -} - -bool Dict::CurrentBestChoiceIs(const WERD_CHOICE &WordChoice) { - return (best_choices_ != NIL_LIST && - StringSameAs(WordChoice, (VIABLE_CHOICE)first_node(best_choices_))); -} - -FLOAT32 Dict::CurrentBestChoiceAdjustFactor() { - VIABLE_CHOICE BestChoice; - if (best_choices_ == NIL_LIST) - return (MAX_FLOAT32); - BestChoice = (VIABLE_CHOICE) first_node (best_choices_); - return (BestChoice->AdjustFactor); -} - - -bool Dict::CurrentWordAmbig() { - return (list_rest (best_choices_) != NIL_LIST); -} - - -void Dict::DebugWordChoices() { - LIST Choices; - int i; - char LabelString[80]; - VIABLE_CHOICE VChoice = (VIABLE_CHOICE)first_node(best_choices_); - bool force_debug = - fragments_debug && VChoice != NULL && VChoice->ComposedFromCharFragments; - - if (stopper_debug_level >= 1 || force_debug || - (((STRING)word_to_debug).length() > 0 && best_choices_ && - StringSameAs(word_to_debug.string(), word_to_debug_lengths.string(), - (VIABLE_CHOICE)first_node(best_choices_)))) { - if (best_raw_choice_) - PrintViableChoice(stderr, "\nBest Raw Choice: ", best_raw_choice_); - - i = 1; - Choices = best_choices_; - if (Choices) - cprintf("\nBest Cooked Choices:\n"); - iterate(Choices) { - sprintf(LabelString, "Cooked Choice #%d: ", i); - PrintViableChoice(stderr, LabelString, - (VIABLE_CHOICE)first_node(Choices)); - i++; - } - } -} - -void Dict::PrintAmbigAlternatives(FILE *file, const char *label, - int label_num_unichars) { - iterate(raw_choices_) { - VIABLE_CHOICE Choice = (VIABLE_CHOICE)first_node(raw_choices_); - if (Choice->Length > 0 && - (label_num_unichars > 1 || Choice->Length > 1)) { - for (int i = 0; i < Choice->Length; i++) { - fprintf(file, "%s", - getUnicharset().id_to_unichar(Choice->Blob[i].Class)); - } - fflush(file); - fprintf(file, "\t%s\t%.4f\t%.4f\n", label, - Choice->Rating, Choice->Certainty); - } - } -} - -void Dict::FilterWordChoices() { - EXPANDED_CHOICE BestChoice; - - if (best_choices_ == NIL_LIST || second_node (best_choices_) == NIL_LIST) - return; - - // Compute certainties and class for each chunk in best choice. - VIABLE_CHOICE_STRUCT *best_choice = - (VIABLE_CHOICE_STRUCT *)first_node(best_choices_); - ExpandChoice(best_choice, &BestChoice); - if (stopper_debug_level >= 2) - PrintViableChoice(stderr, "\nFiltering against best choice: ", best_choice); - TessResultCallback2* is_bad = - NewPermanentTessCallback(this, &Dict::FreeBadChoice); - set_rest(best_choices_, delete_d(list_rest(best_choices_), - &BestChoice, is_bad)); - delete is_bad; -} - -void Dict::FindClassifierErrors(FLOAT32 MinRating, - FLOAT32 MaxRating, - FLOAT32 RatingMargin, - FLOAT32 Thresholds[]) { - EXPANDED_CHOICE BestRaw; - VIABLE_CHOICE Choice; - int i, j, Chunk; - FLOAT32 AvgRating; - int NumErrorChunks; - - assert (best_choices_ != NIL_LIST); - assert (best_raw_choice_ != NULL); - - ExpandChoice(best_raw_choice_, &BestRaw); - Choice = (VIABLE_CHOICE) first_node (best_choices_); - - for (i = 0, Chunk = 0; i < Choice->Length; i++, Thresholds++) { - AvgRating = 0.0; - NumErrorChunks = 0; - - for (j = 0; j < Choice->Blob[i].NumChunks; j++, Chunk++) { - if (Choice->Blob[i].Class != BestRaw.ChunkClass[Chunk]) { - AvgRating += BestRaw.ChunkCertainty[Chunk]; - NumErrorChunks++; - } - } - - if (NumErrorChunks > 0) { - AvgRating /= NumErrorChunks; - *Thresholds = (AvgRating / -certainty_scale) * (1.0 - RatingMargin); - } - else - *Thresholds = MaxRating; - - if (*Thresholds > MaxRating) - *Thresholds = MaxRating; - if (*Thresholds < MinRating) - *Thresholds = MinRating; - } -} - -void Dict::InitChoiceAccum() { - BLOB_WIDTH *BlobWidth, *End; - - if (best_raw_choice_) - delete best_raw_choice_; - best_raw_choice_ = NULL; - - if (best_choices_) - destroy_nodes(best_choices_, DeleteViableChoiceStruct); - best_choices_ = NIL_LIST; - - if (raw_choices_) - destroy_nodes(raw_choices_, DeleteViableChoiceStruct); - raw_choices_ = NIL_LIST; - - EnableChoiceAccum(); - - for (BlobWidth = current_segmentation_, - End = current_segmentation_ + MAX_NUM_CHUNKS; - BlobWidth < End; *BlobWidth++ = 1); - -} - -void Dict::ClearBestChoiceAccum() { - if (best_choices_) destroy_nodes(best_choices_, DeleteViableChoiceStruct); - best_choices_ = NIL_LIST; -} - -void Dict::LogNewSegmentation(PIECES_STATE BlobWidth) { - BLOB_WIDTH *Segmentation; - for (Segmentation = current_segmentation_; *BlobWidth != 0; - BlobWidth++, Segmentation++) - *Segmentation = *BlobWidth; - *Segmentation = 0; -} - -void Dict::LogNewSplit(int Blob) { - LIST Choices; - if (best_raw_choice_) AddNewChunk(best_raw_choice_, Blob); - Choices = best_choices_; - iterate(Choices) { - AddNewChunk ((VIABLE_CHOICE) first_node (Choices), Blob); - } - Choices = raw_choices_; - iterate(Choices) { - AddNewChunk ((VIABLE_CHOICE) first_node (Choices), Blob); - } -} - -void Dict::LogNewChoice(FLOAT32 AdjustFactor, - const float Certainties[], - bool raw_choice, - WERD_CHOICE *WordChoice, - const BLOB_CHOICE_LIST_VECTOR &blob_choices) { - LIST ChoicesList; - LIST Choices; - FLOAT32 Threshold; - - if (!keep_word_choices_) - return; - - if (raw_choice) { - if (!best_raw_choice_) { - best_raw_choice_ = - NewViableChoice(*WordChoice, AdjustFactor, Certainties); - } else if (WordChoice->rating() < best_raw_choice_->Rating) { - if (ChoiceSameAs(*WordChoice, best_raw_choice_)) { - FillViableChoice(*WordChoice, AdjustFactor, Certainties, - best_raw_choice_); - } else { - delete best_raw_choice_; - best_raw_choice_ = - NewViableChoice(*WordChoice, AdjustFactor, Certainties); - } - } - if (!save_raw_choices) return; - ChoicesList = raw_choices_; - } else { - ChoicesList = best_choices_; - } - - // Throw out obviously bad choices to save some work. - if (ChoicesList != NIL_LIST) { - Threshold = StopperAmbigThreshold(BestFactor(ChoicesList), AdjustFactor); - if (Threshold > -stopper_ambiguity_threshold_offset) - Threshold = -stopper_ambiguity_threshold_offset; - if (WordChoice->certainty() - BestCertainty (ChoicesList) < Threshold) { - // Set the rating of the word to be terrible, so that it does not - // get chosen as the best choice. - if (stopper_debug_level >= 2) { - STRING bad_string; - WordChoice->string_and_lengths(&bad_string, NULL); - tprintf("Discarding choice \"%s\" with an overly low certainty" - " %.4f vs best choice certainty %.4f (Threshold: %.4f)\n", - bad_string.string(), WordChoice->certainty(), - BestCertainty(ChoicesList), - Threshold + BestCertainty(ChoicesList)); - } - WordChoice->set_rating(WERD_CHOICE::kBadRating); - return; - } - } - - // See if a choice with the same text string has already been found. - VIABLE_CHOICE NewChoice = NULL; - Choices = ChoicesList; - - iterate(Choices) { - if (ChoiceSameAs (*WordChoice, (VIABLE_CHOICE) first_node (Choices))) { - if (WordChoice->rating() < BestRating (Choices)) { - NewChoice = (VIABLE_CHOICE) first_node (Choices); - } else { - return; - } - } - } - - if (NewChoice) { - FillViableChoice(*WordChoice, AdjustFactor, Certainties, NewChoice); - ChoicesList = delete_d(ChoicesList, NewChoice, is_same_node); - } else { - NewChoice = NewViableChoice(*WordChoice, AdjustFactor, Certainties); - } - - // Now we know we're gonna save it, so add the expensive copy. - NewChoice->SetBlobChoices(blob_choices); - - ChoicesList = s_adjoin (ChoicesList, NewChoice, CmpChoiceRatings); - if (stopper_debug_level >= 2) - raw_choice ? PrintViableChoice (stderr, "New Raw Choice: ", NewChoice) : - PrintViableChoice (stderr, "New Word Choice: ", NewChoice); - if (count (ChoicesList) > tessedit_truncate_wordchoice_log) { - Choices = - (LIST) nth_cell (ChoicesList, tessedit_truncate_wordchoice_log); - destroy_nodes(list_rest (Choices), DeleteViableChoiceStruct); - set_rest(Choices, NIL_LIST); - } - - // Update raw_choices_/best_choices_ pointer. - if (raw_choice) { - raw_choices_ = ChoicesList; - } else { - best_choices_ = ChoicesList; - } -} - bool Dict::NoDangerousAmbig(WERD_CHOICE *best_choice, DANGERR *fixpt, bool fix_replaceable, - BLOB_CHOICE_LIST_VECTOR *blob_choices, - bool *modified_blobs) { + MATRIX *ratings) { if (stopper_debug_level > 2) { tprintf("\nRunning NoDangerousAmbig() for %s\n", best_choice->debug_string().string()); @@ -592,7 +163,6 @@ bool Dict::NoDangerousAmbig(WERD_CHOICE *best_choice, // for each unichar id in BestChoice. BLOB_CHOICE_LIST_VECTOR ambig_blob_choices; int i; - bool modified_best_choice = false; bool ambigs_found = false; // For each position in best_choice: // -- choose AMBIG_SPEC_LIST that corresponds to unichar_id at best_choice[i] @@ -621,10 +191,10 @@ bool Dict::NoDangerousAmbig(WERD_CHOICE *best_choice, for (i = 0; i < best_choice->length(); ++i) { BLOB_CHOICE_LIST *lst = new BLOB_CHOICE_LIST(); BLOB_CHOICE_IT lst_it(lst); - // TODO(rays/antonova) Should these BLOB_CHOICEs use real xheights - // or are these fake ones good enough? + // TODO(rays/antonova) Put real xheights and y shifts here. lst_it.add_to_end(new BLOB_CHOICE(best_choice->unichar_id(i), - 0.0, 0.0, -1, -1, -1, 0, 1, false)); + 0.0, 0.0, -1, -1, -1, 0, 1, 0, + BCC_AMBIG)); ambig_blob_choices.push_back(lst); } } @@ -632,14 +202,15 @@ bool Dict::NoDangerousAmbig(WERD_CHOICE *best_choice, int wrong_ngram_index; int next_index; int blob_index = 0; - for (i = 0; i < best_choice->length(); ++i) { - if (i > 0) blob_index += best_choice->fragment_length(i-1); + for (i = 0; i < best_choice->length(); blob_index += best_choice->state(i), + ++i) { UNICHAR_ID curr_unichar_id = best_choice->unichar_id(i); if (stopper_debug_level > 2) { tprintf("Looking for %s ngrams starting with %s:\n", replace ? "replaceable" : "ambiguous", getUnicharset().debug_str(curr_unichar_id).string()); } + int num_wrong_blobs = best_choice->state(i); wrong_ngram_index = 0; wrong_ngram[wrong_ngram_index] = curr_unichar_id; if (curr_unichar_id == INVALID_UNICHAR_ID || @@ -663,27 +234,31 @@ bool Dict::NoDangerousAmbig(WERD_CHOICE *best_choice, if (compare == 0) { // Record the place where we found an ambiguity. if (fixpt != NULL) { + UNICHAR_ID leftmost_id = ambig_spec->correct_fragments[0]; fixpt->push_back(DANGERR_INFO( - blob_index, blob_index+wrong_ngram_index, replace, - getUnicharset().get_isngram(ambig_spec->correct_ngram_id))); + blob_index, blob_index + num_wrong_blobs, replace, + getUnicharset().get_isngram(ambig_spec->correct_ngram_id), + leftmost_id)); if (stopper_debug_level > 1) { - tprintf("fixpt+=(%d %d %d %d)\n", blob_index, - blob_index+wrong_ngram_index, false, + tprintf("fixpt+=(%d %d %d %d %s)\n", blob_index, + blob_index + num_wrong_blobs, false, getUnicharset().get_isngram( - ambig_spec->correct_ngram_id)); + ambig_spec->correct_ngram_id), + getUnicharset().id_to_unichar(leftmost_id)); } } if (replace) { if (stopper_debug_level > 2) { - tprintf("replace ambiguity with: "); + tprintf("replace ambiguity with %s : ", + getUnicharset().id_to_unichar( + ambig_spec->correct_ngram_id)); UnicharIdArrayUtils::print( ambig_spec->correct_fragments, getUnicharset()); } ReplaceAmbig(i, ambig_spec->wrong_ngram_size, ambig_spec->correct_ngram_id, - best_choice, blob_choices, modified_blobs); - modified_best_choice = true; + best_choice, ratings); } else if (i > 0 || ambig_spec->type != CASE_AMBIG) { // We found dang ambig - update ambig_blob_choices. if (stopper_debug_level > 2) { @@ -704,7 +279,7 @@ bool Dict::NoDangerousAmbig(WERD_CHOICE *best_choice, BLOB_CHOICE_IT bc_it(ambig_blob_choices[i+tmp_index]); bc_it.add_to_end(new BLOB_CHOICE( ambig_spec->correct_fragments[tmp_index], -1.0, 0.0, - -1, -1, -1, 0, 1, false)); + -1, -1, -1, 0, 1, 0, BCC_AMBIG)); } } spec_it.forward(); @@ -715,6 +290,7 @@ bool Dict::NoDangerousAmbig(WERD_CHOICE *best_choice, // more ambigs starting with curr_unichar_id in AMBIG_SPEC_LIST. wrong_ngram[++wrong_ngram_index] = best_choice->unichar_id(next_index); + num_wrong_blobs += best_choice->state(next_index); } else { break; // no more matching ambigs in this AMBIG_SPEC_LIST } @@ -749,19 +325,35 @@ bool Dict::NoDangerousAmbig(WERD_CHOICE *best_choice, // fragments is added to other functions. int orig_i = 0; for (i = 0; i < alt_word->length(); ++i) { + const UNICHARSET &uchset = getUnicharset(); bool replacement_is_ngram = - getUnicharset().get_isngram(alt_word->unichar_id(i)); - int end_i = orig_i + alt_word->fragment_length(i) - 1; - if (alt_word->fragment_length(i) > 1 || - (orig_i == end_i && replacement_is_ngram)) { - fixpt->push_back(DANGERR_INFO(orig_i, end_i, true, - replacement_is_ngram)); - if (stopper_debug_level > 1) { - tprintf("fixpt->dangerous+=(%d %d %d %d)\n", orig_i, end_i, - true, replacement_is_ngram); - } + uchset.get_isngram(alt_word->unichar_id(i)); + UNICHAR_ID leftmost_id = alt_word->unichar_id(i); + if (replacement_is_ngram) { + // we have to extract the leftmost unichar from the ngram. + const char *str = uchset.id_to_unichar(leftmost_id); + int step = uchset.step(str); + if (step) leftmost_id = uchset.unichar_to_id(str, step); } - orig_i += alt_word->fragment_length(i); + int end_i = orig_i + alt_word->state(i); + if (alt_word->state(i) > 1 || + (orig_i + 1 == end_i && replacement_is_ngram)) { + // Compute proper blob indices. + int blob_start = 0; + for (int j = 0; j < orig_i; ++j) + blob_start += best_choice->state(j); + int blob_end = blob_start; + for (int j = orig_i; j < end_i; ++j) + blob_end += best_choice->state(j); + fixpt->push_back(DANGERR_INFO(blob_start, blob_end, true, + replacement_is_ngram, leftmost_id)); + if (stopper_debug_level > 1) { + tprintf("fixpt->dangerous+=(%d %d %d %d %s)\n", orig_i, end_i, + true, replacement_is_ngram, + uchset.id_to_unichar(leftmost_id)); + } + } + orig_i += alt_word->state(i); } } } @@ -785,101 +377,79 @@ void Dict::SettupStopperPass2() { reject_offset_ = stopper_phase2_certainty_rejection_offset; } -void Dict::AddNewChunk(VIABLE_CHOICE Choice, int Blob) { - int i, LastChunk; - for (i = 0, LastChunk = 0; i < Choice->Length; i++) { - LastChunk += Choice->Blob[i].NumChunks; - if (Blob < LastChunk) { - (Choice->Blob[i].NumChunks)++; - return; - } - } - cprintf ("AddNewChunk failed:Choice->Length=%d, LastChunk=%d, Blob=%d\n", - Choice->Length, LastChunk, Blob); - assert(false); // this should never get executed -} - void Dict::ReplaceAmbig(int wrong_ngram_begin_index, int wrong_ngram_size, UNICHAR_ID correct_ngram_id, WERD_CHOICE *werd_choice, - BLOB_CHOICE_LIST_VECTOR *blob_choices, - bool *modified_blobs) { + MATRIX *ratings) { int num_blobs_to_replace = 0; int begin_blob_index = 0; int i; + // Rating and certainty for the new BLOB_CHOICE are derived from the + // replaced choices. + float new_rating = 0.0f; + float new_certainty = 0.0f; + BLOB_CHOICE* old_choice = NULL; for (i = 0; i < wrong_ngram_begin_index + wrong_ngram_size; ++i) { if (i >= wrong_ngram_begin_index) { - num_blobs_to_replace += werd_choice->fragment_length(i); + int num_blobs = werd_choice->state(i); + int col = begin_blob_index + num_blobs_to_replace; + int row = col + num_blobs - 1; + BLOB_CHOICE_LIST* choices = ratings->get(col, row); + ASSERT_HOST(choices != NULL); + old_choice = FindMatchingChoice(werd_choice->unichar_id(i), choices); + ASSERT_HOST(old_choice != NULL); + new_rating += old_choice->rating(); + new_certainty += old_choice->certainty(); + num_blobs_to_replace += num_blobs; } else { - begin_blob_index += werd_choice->fragment_length(i); + begin_blob_index += werd_choice->state(i); } } - BLOB_CHOICE_IT bit; - int temp_blob_index = begin_blob_index; - const char *temp_uch = NULL; - const char *correct_ngram_str = - getUnicharset().id_to_unichar(correct_ngram_id); + new_certainty /= wrong_ngram_size; + // If there is no entry in the ratings matrix, add it. + MATRIX_COORD coord(begin_blob_index, + begin_blob_index + num_blobs_to_replace - 1); + if (!coord.Valid(*ratings)) { + ratings->IncreaseBandSize(coord.row - coord.col + 1); + } + if (ratings->get(coord.col, coord.row) == NULL) + ratings->put(coord.col, coord.row, new BLOB_CHOICE_LIST); + BLOB_CHOICE_LIST* new_choices = ratings->get(coord.col, coord.row); + BLOB_CHOICE* choice = FindMatchingChoice(correct_ngram_id, new_choices); + if (choice != NULL) { + // Already there. Upgrade if new rating better. + if (new_rating < choice->rating()) + choice->set_rating(new_rating); + if (new_certainty < choice->certainty()) + choice->set_certainty(new_certainty); + new_choices->sort(&BLOB_CHOICE::SortByRating); + } else { + // Need a new choice with the correct_ngram_id. + choice = new BLOB_CHOICE(*old_choice); + choice->set_unichar_id(correct_ngram_id); + choice->set_rating(new_rating); + choice->set_certainty(new_certainty); + choice->set_classifier(BCC_AMBIG); + choice->set_matrix_cell(coord.col, coord.row); + new_choices->add_sorted(&BLOB_CHOICE::SortByRating, false, choice); + } + // Remove current unichar from werd_choice. On the last iteration + // set the correct replacement unichar instead of removing a unichar. for (int replaced_count = 0; replaced_count < wrong_ngram_size; ++replaced_count) { - if (blob_choices != NULL) { - UNICHAR_ID uch_id = werd_choice->unichar_id(wrong_ngram_begin_index); - int fraglen = werd_choice->fragment_length(wrong_ngram_begin_index); - if (fraglen > 1) temp_uch = getUnicharset().id_to_unichar(uch_id); - for (i = 0; i < fraglen; ++i) { - if (fraglen > 1) { - STRING frag_str = - CHAR_FRAGMENT::to_string(temp_uch, i, fraglen, false); - getUnicharset().unichar_insert(frag_str.string()); - uch_id = getUnicharset().unichar_to_id(frag_str.string()); - } - bit.set_to_list(blob_choices->get(temp_blob_index)); - STRING correct_frag_uch = - CHAR_FRAGMENT::to_string(correct_ngram_str, - temp_blob_index - begin_blob_index, - num_blobs_to_replace, false); - getUnicharset().unichar_insert(correct_frag_uch.string()); - UNICHAR_ID correct_frag_uch_id = - getUnicharset().unichar_to_id(correct_frag_uch.string()); - // Find the WERD_CHOICE corresponding to the original unichar in - // the list of blob choices, add the derived character fragment - // before it with the same rating and certainty. - for (bit.mark_cycle_pt(); !bit.cycled_list(); bit.forward()) { - if (bit.data()->unichar_id() == correct_frag_uch_id) { - break; // the unichar we want to insert is already there - } - if (bit.data()->unichar_id() == uch_id) { - bit.add_before_then_move(new BLOB_CHOICE(*(bit.data()))); - bit.data()->set_unichar_id(correct_frag_uch_id); - if (modified_blobs != NULL) *modified_blobs = true; - break; - } - } - temp_blob_index++; - } - } - // Remove current unichar from werd_choice. On the last iteration - // set the correct replacement unichar instead of removing a unichar. if (replaced_count + 1 == wrong_ngram_size) { - werd_choice->set_unichar_id(correct_ngram_id, - num_blobs_to_replace, 0.0, 0.0, wrong_ngram_begin_index); + werd_choice->set_blob_choice(wrong_ngram_begin_index, + num_blobs_to_replace, choice); } else { - werd_choice->remove_unichar_id(wrong_ngram_begin_index); + werd_choice->remove_unichar_id(wrong_ngram_begin_index + 1); } } - if (stopper_debug_level >= 1 && modified_blobs != NULL && - *modified_blobs && blob_choices != NULL) { + if (stopper_debug_level >= 1) { werd_choice->print("ReplaceAmbig() "); tprintf("Modified blob_choices: "); - for (int i = 0; i < blob_choices->size(); ++i) { - print_ratings_list("\n", blob_choices->get(i), getUnicharset()); - } + print_ratings_list("\n", new_choices, getUnicharset()); } } -int Dict::ChoiceSameAs(const WERD_CHOICE &WordChoice, - VIABLE_CHOICE ViableChoice) { - return (StringSameAs(WordChoice, ViableChoice)); -} - int Dict::LengthOfShortestAlphaRun(const WERD_CHOICE &WordChoice) { int shortest = MAX_INT32; int curr_len = 0; @@ -899,92 +469,7 @@ int Dict::LengthOfShortestAlphaRun(const WERD_CHOICE &WordChoice) { return shortest; } -VIABLE_CHOICE Dict::NewViableChoice(const WERD_CHOICE &WordChoice, - FLOAT32 AdjustFactor, - const float Certainties[]) { - int Length = WordChoice.length(); - assert (Length <= MAX_NUM_CHUNKS && Length > 0); - VIABLE_CHOICE NewChoice = new VIABLE_CHOICE_STRUCT(Length); - FillViableChoice(WordChoice, AdjustFactor, Certainties, NewChoice); - return NewChoice; -} - -void Dict::PrintViableChoice(FILE *File, const char *Label, VIABLE_CHOICE Choice) { - int i, j; - fprintf (File, "%s", Label); - fprintf(File, "(R=%5.1f, C=%4.1f, F=%4.2f, Frag=%d) ", - Choice->Rating, Choice->Certainty, - Choice->AdjustFactor, Choice->ComposedFromCharFragments); - - for (i = 0; i < Choice->Length; i++) - fprintf(File, "%s", getUnicharset().id_to_unichar(Choice->Blob[i].Class)); - fprintf(File, "\n"); - - for (i = 0; i < Choice->Length; i++) { - fprintf(File, " %s", getUnicharset().id_to_unichar(Choice->Blob[i].Class)); - for (j = 0; j < Choice->Blob[i].NumChunks - 1; j++) - fprintf(File, " "); - } - fprintf(File, "\n"); - - for (i = 0; i < Choice->Length; i++) { - for (j = 0; j < Choice->Blob[i].NumChunks; j++) - fprintf(File, "%3d ", (int) (Choice->Blob[i].Certainty * -10.0)); - } - fprintf(File, "\n"); - - for (i = 0; i < Choice->Length; i++) { - for (j = 0; j < Choice->Blob[i].NumChunks; j++) - fprintf(File, "%3d ", Choice->Blob[i].NumChunks); - } - fprintf(File, "\n"); -} - -void Dict::FillViableChoice(const WERD_CHOICE &WordChoice, - FLOAT32 AdjustFactor, const float Certainties[], - VIABLE_CHOICE ViableChoice) { - ViableChoice->Init(WordChoice, current_segmentation_, Certainties, - AdjustFactor); - -} - -bool Dict::StringSameAs(const WERD_CHOICE &WordChoice, - VIABLE_CHOICE ViableChoice) { - if (WordChoice.length() != ViableChoice->Length) { - return false; - } - int i; - CHAR_CHOICE *CharChoice; - for (i = 0, CharChoice = &(ViableChoice->Blob[0]); - i < ViableChoice->Length; CharChoice++, i++) { - if (CharChoice->Class != WordChoice.unichar_id(i)) { - return false; - } - } - return true; -} - -bool Dict::StringSameAs(const char *String, - const char *String_lengths, - VIABLE_CHOICE ViableChoice) { - CHAR_CHOICE *Char; - int i; - int current_unichar_length; - - for (Char = &(ViableChoice->Blob[0]), i = 0; - i < ViableChoice->Length; - String += *(String_lengths++), Char++, i++) { - current_unichar_length = strlen(getUnicharset().id_to_unichar(Char->Class)); - if (current_unichar_length != *String_lengths || - strncmp(String, getUnicharset().id_to_unichar(Char->Class), - current_unichar_length) != 0) - return false; - } - return (*String == 0) ? true : false; -} - -int Dict::UniformCertainties(const BLOB_CHOICE_LIST_VECTOR &Choices, - const WERD_CHOICE &BestChoice) { +int Dict::UniformCertainties(const WERD_CHOICE& word) { float Certainty; float WorstCertainty = MAX_FLOAT32; float CertaintyThreshold; @@ -992,17 +477,14 @@ int Dict::UniformCertainties(const BLOB_CHOICE_LIST_VECTOR &Choices, FLOAT64 TotalCertaintySquared; FLOAT64 Variance; FLOAT32 Mean, StdDev; - int WordLength; + int word_length = word.length(); - WordLength = Choices.length(); - if (WordLength < 3) + if (word_length < 3) return true; TotalCertainty = TotalCertaintySquared = 0.0; - BLOB_CHOICE_IT BlobChoiceIt; - for (int i = 0; i < Choices.length(); ++i) { - BlobChoiceIt.set_to_list(Choices.get(i)); - Certainty = BlobChoiceIt.data()->certainty(); + for (int i = 0; i < word_length; ++i) { + Certainty = word.certainty(i); TotalCertainty += Certainty; TotalCertaintySquared += Certainty * Certainty; if (Certainty < WorstCertainty) @@ -1010,14 +492,14 @@ int Dict::UniformCertainties(const BLOB_CHOICE_LIST_VECTOR &Choices, } // Subtract off worst certainty from statistics. - WordLength--; + word_length--; TotalCertainty -= WorstCertainty; TotalCertaintySquared -= WorstCertainty * WorstCertainty; - Mean = TotalCertainty / WordLength; - Variance = ((WordLength * TotalCertaintySquared - + Mean = TotalCertainty / word_length; + Variance = ((word_length * TotalCertaintySquared - TotalCertainty * TotalCertainty) / - (WordLength * (WordLength - 1))); + (word_length * (word_length - 1))); if (Variance < 0.0) Variance = 0.0; StdDev = sqrt (Variance); @@ -1026,11 +508,11 @@ int Dict::UniformCertainties(const BLOB_CHOICE_LIST_VECTOR &Choices, if (CertaintyThreshold > stopper_nondict_certainty_base) CertaintyThreshold = stopper_nondict_certainty_base; - if (BestChoice.certainty() < CertaintyThreshold) { + if (word.certainty() < CertaintyThreshold) { if (stopper_debug_level >= 1) - cprintf("Stopper: Non-uniform certainty = %4.1f" + tprintf("Stopper: Non-uniform certainty = %4.1f" " (m=%4.1f, s=%4.1f, t=%4.1f)\n", - BestChoice.certainty(), Mean, StdDev, CertaintyThreshold); + word.certainty(), Mean, StdDev, CertaintyThreshold); return false; } else { return true; diff --git a/dict/stopper.h b/dict/stopper.h index f69be15b53..a2d7b3621d 100644 --- a/dict/stopper.h +++ b/dict/stopper.h @@ -25,7 +25,6 @@ #include "genericvector.h" #include "params.h" #include "ratngs.h" -#include "states.h" #include "unichar.h" class WERD_CHOICE; @@ -34,55 +33,18 @@ typedef uinT8 BLOB_WIDTH; struct DANGERR_INFO { DANGERR_INFO() : - begin(-1), end(-1), dangerous(false), correct_is_ngram(false) {} - DANGERR_INFO(int b, int e, bool d, bool n) : - begin(b), end(e), dangerous(d), correct_is_ngram(n) {} + begin(-1), end(-1), dangerous(false), correct_is_ngram(false), + leftmost(INVALID_UNICHAR_ID) {} + DANGERR_INFO(int b, int e, bool d, bool n, UNICHAR_ID l) : + begin(b), end(e), dangerous(d), correct_is_ngram(n), leftmost(l) {} int begin; int end; bool dangerous; bool correct_is_ngram; + UNICHAR_ID leftmost; // in the replacement, what's the leftmost character? }; typedef GenericVector DANGERR; -enum ACCEPTABLE_CHOICE_CALLER { CHOPPER_CALLER, ASSOCIATOR_CALLER }; - -struct CHAR_CHOICE { - UNICHAR_ID Class; - uinT16 NumChunks; - float Certainty; -}; - -class VIABLE_CHOICE_STRUCT { - public: - VIABLE_CHOICE_STRUCT(); - explicit VIABLE_CHOICE_STRUCT(int length); - ~VIABLE_CHOICE_STRUCT(); - - // Fill in the data with these values. - void Init(const WERD_CHOICE& word_choice, - const PIECES_STATE& pieces_state, - const float certainties[], - FLOAT32 adjust_factor); - void SetBlobChoices(const BLOB_CHOICE_LIST_VECTOR &src_choices); - - int Length; - float Rating; - float Certainty; - FLOAT32 AdjustFactor; - bool ComposedFromCharFragments; - CHAR_CHOICE *Blob; - BLOB_CHOICE_LIST_CLIST *blob_choices; - - private: - // Disallow assignment and copy construction - VIABLE_CHOICE_STRUCT(const VIABLE_CHOICE_STRUCT &other) - : Length(0), Blob(NULL) {} - VIABLE_CHOICE_STRUCT &operator=(const VIABLE_CHOICE_STRUCT &other) { - return *this; - } -}; - -typedef VIABLE_CHOICE_STRUCT *VIABLE_CHOICE; #endif diff --git a/dict/trie.cpp b/dict/trie.cpp index ededbaf121..189c8c9b70 100644 --- a/dict/trie.cpp +++ b/dict/trie.cpp @@ -37,6 +37,7 @@ #include "freelist.h" #include "genericvector.h" #include "helpers.h" +#include "kdpair.h" namespace tesseract { @@ -65,6 +66,7 @@ const char *Trie::get_reverse_policy_name(RTLReversePolicy reverse_policy) { void Trie::clear() { nodes_.delete_data_pointers(); nodes_.clear(); + root_back_freelist_.clear(); num_edges_ = 0; new_dawg_node(); // Need to allocate node 0. } @@ -85,7 +87,7 @@ bool Trie::edge_char_of(NODE_REF node_ref, NODE_REF next_node, EDGE_VECTOR &vec = (direction == FORWARD_EDGE) ? nodes_[node_ref]->forward_edges : nodes_[node_ref]->backward_edges; int vec_size = vec.size(); - if (node_ref == 0) { // binary search + if (node_ref == 0 && direction == FORWARD_EDGE) { // binary search EDGE_INDEX start = 0; EDGE_INDEX end = vec_size - 1; EDGE_INDEX k; @@ -127,7 +129,7 @@ bool Trie::add_edge_linkage(NODE_REF node1, NODE_REF node2, bool marker_flag, EDGE_VECTOR *vec = (direction == FORWARD_EDGE) ? &(nodes_[node1]->forward_edges) : &(nodes_[node1]->backward_edges); int search_index; - if (node1 == 0) { + if (node1 == 0 && direction == FORWARD_EDGE) { search_index = 0; // find the index to make the add sorted while (search_index < vec->size() && given_greater_than_edge_rec(node2, word_end, unichar_id, @@ -139,7 +141,11 @@ bool Trie::add_edge_linkage(NODE_REF node1, NODE_REF node2, bool marker_flag, } EDGE_RECORD edge_rec; link_edge(&edge_rec, node2, marker_flag, direction, word_end, unichar_id); - if (search_index < vec->size()) { + if (node1 == 0 && direction == BACKWARD_EDGE && + !root_back_freelist_.empty()) { + EDGE_INDEX edge_index = root_back_freelist_.pop_back(); + (*vec)[edge_index] = edge_rec; + } else if (search_index < vec->size()) { vec->insert(edge_rec, search_index); } else { vec->push_back(edge_rec); @@ -208,10 +214,18 @@ bool Trie::add_word_to_dawg(const WERD_CHOICE &word, if (!found) { still_finding_chars = false; } else if (next_node_from_edge_rec(*edge_ptr) == 0) { + // We hit the end of an existing word, but the new word is longer. + // In this case we have to disconnect the existing word from the + // backwards root node, mark the current position as end-of-word + // and add new nodes for the increased length. Disconnecting the + // existing word from the backwards root node requires a linear + // search, so it is much faster to add the longest words first, + // to avoid having to come here. word_end = true; still_finding_chars = false; remove_edge(last_node, 0, word_end, unichar_id); } else { + // We have to add a new branch here for the new word. if (marker_flag) set_marker_flag_in_edge_rec(edge_ptr); last_node = next_node_from_edge_rec(*edge_ptr); } @@ -245,6 +259,9 @@ bool Trie::add_word_to_dawg(const WERD_CHOICE &word, add_word_ending(edge_ptr, next_node_from_edge_rec(*edge_ptr), marker_flag, unichar_id); } else { + // Add a link to node 0. All leaves connect to node 0 so the back links can + // be used in reduction to a dawg. This root backward node has one edge + // entry for every word, (except prefixes of longer words) so it is huge. if (!add_failed && !add_new_edge(last_node, the_next_node, marker_flag, true, unichar_id)) add_failed = true; @@ -265,14 +282,33 @@ NODE_REF Trie::new_dawg_node() { return nodes_.length() - 1; } +// Sort function to sort words by decreasing order of length. +static int sort_strings_by_dec_length(const void* v1, const void* v2) { + const STRING* s1 = reinterpret_cast(v1); + const STRING* s2 = reinterpret_cast(v2); + return s2->length() - s1->length(); +} + +bool Trie::read_and_add_word_list(const char *filename, + const UNICHARSET &unicharset, + Trie::RTLReversePolicy reverse_policy) { + GenericVector word_list; + if (!read_word_list(filename, unicharset, reverse_policy, &word_list)) + return false; + word_list.sort(sort_strings_by_dec_length); + return add_word_list(word_list, unicharset); +} + bool Trie::read_word_list(const char *filename, const UNICHARSET &unicharset, - Trie::RTLReversePolicy reverse_policy) { + Trie::RTLReversePolicy reverse_policy, + GenericVector* words) { FILE *word_file; char string[CHARS_PER_LINE]; int word_count = 0; - word_file = open_file (filename, "r"); + word_file = fopen(filename, "rb"); + if (word_file == NULL) return false; while (fgets(string, CHARS_PER_LINE, word_file) != NULL) { chomp_string(string); // remove newline @@ -286,13 +322,7 @@ bool Trie::read_word_list(const char *filename, if (debug_level_ && word_count % 10000 == 0) tprintf("Read %d words so far\n", word_count); if (word.length() != 0 && !word.contains_unichar_id(INVALID_UNICHAR_ID)) { - if (!this->word_in_dawg(word)) { - this->add_word_to_dawg(word); - if (!this->word_in_dawg(word)) { - tprintf("Error: word '%s' not in DAWG after adding it\n", string); - return false; - } - } + words->push_back(word.unichar_string()); } else if (debug_level_) { tprintf("Skipping invalid word %s\n", string); if (debug_level_ >= 3) word.print(); @@ -304,6 +334,22 @@ bool Trie::read_word_list(const char *filename, return true; } +bool Trie::add_word_list(const GenericVector& words, + const UNICHARSET &unicharset) { + for (int i = 0; i < words.size(); ++i) { + WERD_CHOICE word(words[i].string(), unicharset); + if (!word_in_dawg(word)) { + add_word_to_dawg(word); + if (!word_in_dawg(word)) { + tprintf("Error: word '%s' not in DAWG after adding it\n", + words[i].string()); + return false; + } + } + } + return true; +} + void Trie::initialize_patterns(UNICHARSET *unicharset) { unicharset->unichar_insert(kAlphaPatternUnicode); alpha_pattern_ = unicharset->unichar_to_id(kAlphaPatternUnicode); @@ -368,7 +414,7 @@ bool Trie::read_pattern_list(const char *filename, return false; } - FILE *pattern_file = open_file (filename, "r"); + FILE *pattern_file = fopen(filename, "rb"); if (pattern_file == NULL) { tprintf("Error opening pattern file %s\n", filename); return false; @@ -456,13 +502,30 @@ void Trie::remove_edge_linkage(NODE_REF node1, NODE_REF node2, int direction, } if (direction == FORWARD_EDGE) { nodes_[node1]->forward_edges.remove(edge_index); + } else if (node1 == 0) { + KillEdge(&nodes_[node1]->backward_edges[edge_index]); + root_back_freelist_.push_back(edge_index); } else { nodes_[node1]->backward_edges.remove(edge_index); } --num_edges_; } +// Some optimizations employed in add_word_to_dawg and trie_to_dawg: +// 1 Avoid insertion sorting or bubble sorting the tail root node +// (back links on node 0, a list of all the leaves.). The node is +// huge, and sorting it with n^2 time is terrible. +// 2 Avoid using GenericVector::remove on the tail root node. +// (a) During add of words to the trie, zero-out the unichars and +// keep a freelist of spaces to re-use. +// (b) During reduction, just zero-out the unichars of deleted back +// links, skipping zero entries while searching. +// 3 Avoid linear search of the tail root node. This has to be done when +// a suffix is added to an existing word. Adding words by decreasing +// length avoids this problem entirely. Words can still be added in +// any order, but it is faster to add the longest first. SquishedDawg *Trie::trie_to_dawg() { + root_back_freelist_.clear(); // Will be invalided by trie_to_dawg. if (debug_level_ > 2) { print_all("Before reduction:", MAX_NODE_EDGES_DISPLAY); } @@ -528,11 +591,7 @@ bool Trie::eliminate_redundant_edges(NODE_REF node, EDGE_RECORD *edge_ptr = NULL; EDGE_INDEX edge_index; int i; - // Remove the backward link in node to next_node2. - const EDGE_RECORD &fwd_edge = next_node2_ptr->forward_edges[0]; - remove_edge_linkage(node, next_node2, BACKWARD_EDGE, - end_of_word_from_edge_rec(fwd_edge), - unichar_id_from_edge_rec(fwd_edge)); + // The backward link in node to next_node2 will be zeroed out by the caller. // Copy all the backward links in next_node2 to node next_node1 for (i = 0; i < next_node2_ptr->backward_edges.size(); ++i) { const EDGE_RECORD &bkw_edge = next_node2_ptr->backward_edges[i]; @@ -563,32 +622,38 @@ bool Trie::eliminate_redundant_edges(NODE_REF node, bool Trie::reduce_lettered_edges(EDGE_INDEX edge_index, UNICHAR_ID unichar_id, NODE_REF node, - const EDGE_VECTOR &backward_edges, + EDGE_VECTOR* backward_edges, NODE_MARKER reduced_nodes) { if (debug_level_ > 1) tprintf("reduce_lettered_edges(edge=" REFFORMAT ")\n", edge_index); // Compare each of the edge pairs with the given unichar_id. bool did_something = false; - for (int i = edge_index; i < backward_edges.size() - 1; ++i) { + for (int i = edge_index; i < backward_edges->size() - 1; ++i) { // Find the first edge that can be eliminated. UNICHAR_ID curr_unichar_id = INVALID_UNICHAR_ID; - while (i < backward_edges.size() && - ((curr_unichar_id = unichar_id_from_edge_rec(backward_edges[i])) == - unichar_id) && - !can_be_eliminated(backward_edges[i])) ++i; - if (i == backward_edges.size() || curr_unichar_id != unichar_id) break; - const EDGE_RECORD &edge_rec = backward_edges[i]; + while (i < backward_edges->size()) { + curr_unichar_id = unichar_id_from_edge_rec((*backward_edges)[i]); + if (curr_unichar_id != 0) { + if (curr_unichar_id != unichar_id) return did_something; + if (can_be_eliminated((*backward_edges)[i])) break; + } + ++i; + } + if (i == backward_edges->size()) break; + const EDGE_RECORD &edge_rec = (*backward_edges)[i]; // Compare it to the rest of the edges with the given unichar_id. - for (int j = i + 1; j < backward_edges.size(); ++j) { - const EDGE_RECORD &next_edge_rec = backward_edges[j]; - if (unichar_id_from_edge_rec(next_edge_rec) != unichar_id) break; + for (int j = i + 1; j < backward_edges->size(); ++j) { + const EDGE_RECORD &next_edge_rec = (*backward_edges)[j]; + UNICHAR_ID next_id = unichar_id_from_edge_rec(next_edge_rec); + if (next_id == 0) continue; + if (next_id != unichar_id) break; if (end_of_word_from_edge_rec(next_edge_rec) == end_of_word_from_edge_rec(edge_rec) && can_be_eliminated(next_edge_rec) && eliminate_redundant_edges(node, edge_rec, next_edge_rec)) { reduced_nodes[next_node_from_edge_rec(edge_rec)] = 0; did_something = true; - --j; // do not increment j if next_edge_rec was removed + KillEdge(&(*backward_edges)[j]); } } } @@ -598,18 +663,15 @@ bool Trie::reduce_lettered_edges(EDGE_INDEX edge_index, void Trie::sort_edges(EDGE_VECTOR *edges) { int num_edges = edges->size(); if (num_edges <= 1) return; - for (int i = 0; i < num_edges - 1; ++i) { - int min = i; - for (int j = (i + 1); j < num_edges; ++j) { - if (unichar_id_from_edge_rec((*edges)[j]) < - unichar_id_from_edge_rec((*edges)[min])) min = j; - } - if (i != min) { - EDGE_RECORD temp = (*edges)[i]; - (*edges)[i] = (*edges)[min]; - (*edges)[min] = temp; - } - } + GenericVector > sort_vec; + sort_vec.reserve(num_edges); + for (int i = 0; i < num_edges; ++i) { + sort_vec.push_back(KDPairInc( + unichar_id_from_edge_rec((*edges)[i]), (*edges)[i])); + } + sort_vec.sort(); + for (int i = 0; i < num_edges; ++i) + (*edges)[i] = sort_vec[i].data; } void Trie::reduce_node_input(NODE_REF node, @@ -620,15 +682,17 @@ void Trie::reduce_node_input(NODE_REF node, } EDGE_VECTOR &backward_edges = nodes_[node]->backward_edges; - if (node != 0) sort_edges(&backward_edges); + sort_edges(&backward_edges); EDGE_INDEX edge_index = 0; while (edge_index < backward_edges.size()) { UNICHAR_ID unichar_id = unichar_id_from_edge_rec(backward_edges[edge_index]); while (reduce_lettered_edges(edge_index, unichar_id, node, - backward_edges, reduced_nodes)); - while (++edge_index < backward_edges.size() && - unichar_id_from_edge_rec(backward_edges[edge_index]) == unichar_id); + &backward_edges, reduced_nodes)); + while (++edge_index < backward_edges.size()) { + UNICHAR_ID id = unichar_id_from_edge_rec(backward_edges[edge_index]); + if (id != 0 && id != unichar_id) break; + } } reduced_nodes[node] = true; // mark as reduced @@ -669,4 +733,5 @@ void Trie::print_node(NODE_REF node, int max_num_edges) const { tprintf("\n"); } } + } // namespace tesseract diff --git a/dict/trie.h b/dict/trie.h index bf1bfb83b1..211e2f0237 100644 --- a/dict/trie.h +++ b/dict/trie.h @@ -114,12 +114,15 @@ class Trie : public Dawg { * Fills the given NodeChildVector with all the unichar ids (and the * corresponding EDGE_REFs) for which there is an edge out of this node. */ - void unichar_ids_of(NODE_REF node, NodeChildVector *vec) const { + void unichar_ids_of(NODE_REF node, NodeChildVector *vec, + bool word_end) const { const EDGE_VECTOR &forward_edges = nodes_[static_cast(node)]->forward_edges; for (int i = 0; i < forward_edges.size(); ++i) { - vec->push_back(NodeChild(unichar_id_from_edge_rec(forward_edges[i]), - make_edge_ref(node, i))); + if (!word_end || end_of_word_from_edge_rec(forward_edges[i])) { + vec->push_back(NodeChild(unichar_id_from_edge_rec(forward_edges[i]), + make_edge_ref(node, i))); + } } } @@ -146,6 +149,10 @@ class Trie : public Dawg { if (edge_ref == NO_EDGE || num_edges_ == 0) return INVALID_UNICHAR_ID; return unichar_id_from_edge_rec(*deref_edge_ref(edge_ref)); } + // Sets the UNICHAR_ID in the given edge_rec to 0, marking the edge dead. + void KillEdge(EDGE_RECORD* edge_rec) const { + *edge_rec &= ~letter_mask_; + } // Prints the contents of the node indicated by the given NODE_REF. // At most max_num_edges will be printed. @@ -157,12 +164,26 @@ class Trie : public Dawg { // with the returned SquishedDawg pointer. SquishedDawg *trie_to_dawg(); - // Inserts the list of words from the given file into the Trie. - // If reverse is true, calls WERD_CHOICE::reverse_unichar_ids_if_rtl() - // on each word before inserting it into the Trie. + // Reads a list of words from the given file and adds into the Trie. + // Calls WERD_CHOICE::reverse_unichar_ids_if_rtl() according to the reverse + // policy and information in the unicharset. + // Returns false on error. + bool read_and_add_word_list(const char *filename, + const UNICHARSET &unicharset, + Trie::RTLReversePolicy reverse); + + // Reads a list of words from the given file, applying the reverse_policy, + // according to information in the unicharset. + // Returns false on error. bool read_word_list(const char *filename, const UNICHARSET &unicharset, - Trie::RTLReversePolicy reverse); + Trie::RTLReversePolicy reverse_policy, + GenericVector* words); + // Adds a list of words previously read using read_word_list to the trie + // using the given unicharset to convert to unichar-ids. + // Returns false on error. + bool add_word_list(const GenericVector& words, + const UNICHARSET &unicharset); // Inserts the list of patterns from the given file into the Trie. // The pattern list file should contain one pattern per line in UTF-8 format. @@ -374,7 +395,7 @@ class Trie : public Dawg { bool reduce_lettered_edges(EDGE_INDEX edge_index, UNICHAR_ID unichar_id, NODE_REF node, - const EDGE_VECTOR &backward_edges, + EDGE_VECTOR* backward_edges, NODE_MARKER reduced_nodes); /** @@ -397,6 +418,8 @@ class Trie : public Dawg { uinT64 max_num_edges_; // maximum number of edges allowed uinT64 deref_direction_mask_; // mask for EDGE_REF to extract direction uinT64 deref_node_index_mask_; // mask for EDGE_REF to extract node index + // Freelist of edges in the root backwards node that were previously zeroed. + GenericVector root_back_freelist_; // Variables for translating character class codes denoted in user patterns // file to the unichar ids used to represent them in a Trie. bool initialized_patterns_; diff --git a/textord/Makefile.am b/textord/Makefile.am index 52230cc55a..3841c6e196 100644 --- a/textord/Makefile.am +++ b/textord/Makefile.am @@ -1,4 +1,5 @@ AM_CPPFLAGS += \ + -DUSE_STD_NAMESPACE \ -I$(top_srcdir)/ccstruct -I$(top_srcdir)/ccutil \ -I$(top_srcdir)/image -I$(top_srcdir)/viewer \ -I$(top_srcdir)/ccmain -I$(top_srcdir)/wordrec -I$(top_srcdir)/api \ @@ -11,7 +12,7 @@ endif noinst_HEADERS = \ - alignedblob.h bbgrid.h blkocc.h blobgrid.h \ + alignedblob.h baselinedetect.h bbgrid.h blkocc.h blobgrid.h \ ccnontextdetect.h cjkpitch.h colfind.h colpartition.h colpartitionset.h \ colpartitiongrid.h \ devanagari_processing.h drawedg.h drawtord.h edgblob.h edgloop.h \ @@ -40,7 +41,7 @@ libtesseract_textord_la_LIBADD = \ endif libtesseract_textord_la_SOURCES = \ - alignedblob.cpp bbgrid.cpp blkocc.cpp blobgrid.cpp \ + alignedblob.cpp baselinedetect.cpp bbgrid.cpp blkocc.cpp blobgrid.cpp \ ccnontextdetect.cpp cjkpitch.cpp colfind.cpp colpartition.cpp colpartitionset.cpp \ colpartitiongrid.cpp devanagari_processing.cpp \ drawedg.cpp drawtord.cpp edgblob.cpp edgloop.cpp \ diff --git a/textord/bbgrid.h b/textord/bbgrid.h index d1a9e18ce2..d2e7710988 100644 --- a/textord/bbgrid.h +++ b/textord/bbgrid.h @@ -31,7 +31,18 @@ #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif - +#ifdef USE_STD_NAMESPACE +#ifdef __linux__ +// hash_set is deprecated in gcc +#include +using __gnu_cxx::hash_set; +#else +#include +using std::hash_set; +#endif +#else +#include +#endif #include "allheaders.h" class BLOCK; @@ -228,6 +239,14 @@ template class BBGrid private: }; +// Hash functor for generic pointers. +template struct PtrHash { + size_t operator()(const T* ptr) const { + return reinterpret_cast(ptr) / sizeof(T); + } +}; + + // The GridSearch class enables neighbourhood searching on a BBGrid. template class GridSearch { public: @@ -360,8 +379,8 @@ template class GridSearch { BBC* next_return_; // Current value of it_.data() used for repositioning. // An iterator over the list at (x_, y_) in the grid_. BBC_C_IT it_; - // List of unique returned elements used when unique_mode_ is true. - BBC_CLIST returns_; + // Set of unique returned elements used when unique_mode_ is true. + hash_set > returns_; }; // Sort function to sort a BBC by bounding_box().left(). @@ -734,8 +753,9 @@ BBC* GridSearch::NextRadSearch() { SetIterator(); } CommonNext(); - } while (unique_mode_ && - !returns_.add_sorted(SortByBoxLeft, true, previous_return_)); + } while (unique_mode_ && returns_.find(previous_return_) != returns_.end()); + if (unique_mode_) + returns_.insert(previous_return_); return previous_return_; } @@ -775,8 +795,9 @@ BBC* GridSearch::NextSideSearch(bool right_to_left) { SetIterator(); } CommonNext(); - } while (unique_mode_ && - !returns_.add_sorted(SortByBoxLeft, true, previous_return_)); + } while (unique_mode_ && returns_.find(previous_return_) != returns_.end()); + if (unique_mode_) + returns_.insert(previous_return_); return previous_return_; } @@ -816,8 +837,9 @@ BBC* GridSearch::NextVerticalSearch( SetIterator(); } CommonNext(); - } while (unique_mode_ && - !returns_.add_sorted(SortByBoxLeft, true, previous_return_)); + } while (unique_mode_ && returns_.find(previous_return_) != returns_.end()); + if (unique_mode_) + returns_.insert(previous_return_); return previous_return_; } @@ -850,8 +872,9 @@ BBC* GridSearch::NextRectSearch() { } CommonNext(); } while (!rect_.overlap(previous_return_->bounding_box()) || - (unique_mode_ && - !returns_.add_sorted(SortByBoxLeft, true, previous_return_))); + (unique_mode_ && returns_.find(previous_return_) != returns_.end())); + if (unique_mode_) + returns_.insert(previous_return_); return previous_return_; } @@ -888,7 +911,7 @@ template void GridSearch::RepositionIterator() { // Something was deleted, so we have little choice but to clear the // returns list. - returns_.shallow_clear(); + returns_.clear(); // Reset the iterator back to one past the previous return. // If the previous_return_ is no longer in the list, then // next_return_ serves as a backup. @@ -921,7 +944,7 @@ void GridSearch::CommonStart(int x, int y) { SetIterator(); previous_return_ = NULL; next_return_ = it_.empty() ? NULL : it_.data(); - returns_.shallow_clear(); + returns_.clear(); } // Factored out helper to complete a next search. diff --git a/textord/colpartition.cpp b/textord/colpartition.cpp index 0a3a51d4c4..fa87272a42 100644 --- a/textord/colpartition.cpp +++ b/textord/colpartition.cpp @@ -310,8 +310,8 @@ void ColPartition::DeleteBoxes() { // this function is assumed to be used shortly after initial creation, which // is before a lot of the members are used. void ColPartition::ReflectInYAxis() { - ColPartition_CLIST reversed_boxes; - ColPartition_C_IT reversed_it(&reversed_boxes); + BLOBNBOX_CLIST reversed_boxes; + BLOBNBOX_C_IT reversed_it(&reversed_boxes); // Reverse the order of the boxes_. BLOBNBOX_C_IT bb_it(&boxes_); for (bb_it.mark_cycle_pt(); !bb_it.cycled_list(); bb_it.forward()) { @@ -961,6 +961,7 @@ void ColPartition::SetPartitionType(int resolution, ColPartitionSet* columns) { ColumnSpanningType span_type = columns->SpanningType(resolution, bounding_box_.left(), bounding_box_.right(), + MIN(bounding_box_.height(), bounding_box_.width()), MidY(), left_margin_, right_margin_, &first_column_, &last_column_, &first_spanned_col); @@ -1044,6 +1045,7 @@ void ColPartition::ColumnRange(int resolution, ColPartitionSet* columns, ColumnSpanningType span_type = columns->SpanningType(resolution, bounding_box_.left(), bounding_box_.right(), + MIN(bounding_box_.height(), bounding_box_.width()), MidY(), left_margin_, right_margin_, first_col, last_col, &first_spanned_col); @@ -1361,8 +1363,7 @@ void ColPartition::AddToWorkingSet(const ICOORD& bleft, const ICOORD& tright, work_set = it.data(); // If last_column_ != first_column, then we need to scoop up all blocks // between here and the last_column_ and put back in work_set. - if (!it.cycled_list() && last_column_ != first_column_ && - !IsPulloutType()) { + if (!it.cycled_list() && last_column_ != first_column_ && !IsPulloutType()) { // Find the column that the right edge falls in. BLOCK_LIST completed_blocks; TO_BLOCK_LIST to_blocks; @@ -1913,7 +1914,7 @@ void ColPartition::RefinePartnersByType(bool upper, } ColPartition_C_IT it(partners); // Purify text by type. - if (!IsImageType()) { + if (!IsImageType() && !IsLineType() && type() != PT_TABLE) { // Keep only partners matching type_. // Exception: PT_VERTICAL_TEXT is allowed to stay with the other // text types if it is the only partner. diff --git a/textord/colpartitionset.cpp b/textord/colpartitionset.cpp index a85ff67b70..8f06a40a00 100644 --- a/textord/colpartitionset.cpp +++ b/textord/colpartitionset.cpp @@ -392,7 +392,8 @@ void ColPartitionSet::DisplayColumnEdges(int y_bottom, int y_top, // represent the gaps in between columns, with 0 being left of the leftmost. // resolution refers to the ppi resolution of the image. ColumnSpanningType ColPartitionSet::SpanningType(int resolution, - int left, int right, int y, + int left, int right, + int height, int y, int left_margin, int right_margin, int* first_col, @@ -406,13 +407,15 @@ ColumnSpanningType ColPartitionSet::SpanningType(int resolution, int col_index = 1; for (it.mark_cycle_pt(); !it.cycled_list(); it.forward(), col_index += 2) { ColPartition* part = it.data(); - if (part->ColumnContains(left, y)) { + if (part->ColumnContains(left, y) || + (it.at_first() && part->ColumnContains(left + height, y))) { // In the default case, first_col is set, but columns_spanned remains // zero, so first_col will get reset in the first column genuinely // spanned, but we can tell the difference from a noise partition // that touches no column. *first_col = col_index; - if (part->ColumnContains(right, y)) { + if (part->ColumnContains(right, y) || + (it.at_last() && part->ColumnContains(right - height, y))) { // Both within a single column. *last_col = col_index; return CST_FLOWING; @@ -422,7 +425,8 @@ ColumnSpanningType ColPartitionSet::SpanningType(int resolution, *first_spanned_col = col_index; margin_columns = 1; } - } else if (part->ColumnContains(right, y)) { + } else if (part->ColumnContains(right, y) || + (it.at_last() && part->ColumnContains(right - height, y))) { if (*first_col < 0) { // It started in-between. *first_col = col_index - 1; diff --git a/textord/colpartitionset.h b/textord/colpartitionset.h index 99ffce6a08..4c26166537 100644 --- a/textord/colpartitionset.h +++ b/textord/colpartitionset.h @@ -104,7 +104,7 @@ class ColPartitionSet : public ELIST_LINK { // resolution refers to the ppi resolution of the image. It may be 0 if only // the first_col and last_col are required. ColumnSpanningType SpanningType(int resolution, - int left, int right, int y, + int left, int right, int height, int y, int left_margin, int right_margin, int* first_col, int* last_col, int* first_spanned_col); diff --git a/textord/drawtord.cpp b/textord/drawtord.cpp index 086ea5dc7f..3e08fdfcc4 100644 --- a/textord/drawtord.cpp +++ b/textord/drawtord.cpp @@ -45,10 +45,12 @@ EXTERN ScrollView* to_win = NULL; **********************************************************************/ #ifndef GRAPHICS_DISABLED -void create_to_win(ICOORD page_tr) { +ScrollView* create_to_win(ICOORD page_tr) { + if (to_win != NULL) return to_win; to_win = new ScrollView(TO_WIN_NAME, TO_WIN_XPOS, TO_WIN_YPOS, page_tr.x() + 1, page_tr.y() + 1, page_tr.x(), page_tr.y(), true); + return to_win; } diff --git a/textord/drawtord.h b/textord/drawtord.h index 2bce907628..f19d1adc23 100644 --- a/textord/drawtord.h +++ b/textord/drawtord.h @@ -33,10 +33,9 @@ extern STRING_VAR_H (to_debugfile, DEBUG_WIN_NAME, "Name of debugfile"); extern STRING_VAR_H (to_smdfile, NO_SMD, "Name of SMD file"); extern ScrollView* to_win; extern FILE *to_debug; -void create_to_win( //make features win - ICOORD page_tr //size of page - ); -void close_to_win(); //make features win +// Creates a static display window for textord, and returns a pointer to it. +ScrollView* create_to_win(ICOORD page_tr); +void close_to_win(); // Destroy the textord window. void create_todebug_win(); //make gradients win void plot_box_list( //make gradients win ScrollView* win, //window to draw in diff --git a/textord/edgblob.cpp b/textord/edgblob.cpp index 020faa0d86..7e2907e7f8 100644 --- a/textord/edgblob.cpp +++ b/textord/edgblob.cpp @@ -179,7 +179,7 @@ inT32 OL_BUCKETS::outline_complexity( * * Find number of descendants of this outline. */ - +// TODO(rays) Merge with outline_complexity. inT32 OL_BUCKETS::count_children( // recursive count C_OUTLINE *outline, // parent outline inT32 max_count // max output @@ -409,7 +409,6 @@ void empty_buckets( // find blobs C_OUTLINE_IT out_it = &outlines; C_OUTLINE_IT bucket_it = buckets->start_scan(); C_OUTLINE_IT parent_it; // parent outline - C_BLOB *blob; // new blob C_BLOB_IT good_blobs = block->blob_list(); C_BLOB_IT junk_blobs = block->reject_blobs(); @@ -426,11 +425,8 @@ void empty_buckets( // find blobs // move to new list out_it.add_after_then_move(parent_it.extract()); good_blob = capture_children(buckets, &junk_blobs, &out_it); - blob = new C_BLOB(&outlines); - if (good_blob) - good_blobs.add_after_then_move(blob); - else - junk_blobs.add_after_then_move(blob); + C_BLOB::ConstructBlobsFromOutlines(good_blob, &outlines, &good_blobs, + &junk_blobs); bucket_it.set_to_list(buckets->scan_next()); } diff --git a/textord/fpchop.cpp b/textord/fpchop.cpp index 1332fb1d07..4f7f89723a 100644 --- a/textord/fpchop.cpp +++ b/textord/fpchop.cpp @@ -133,9 +133,9 @@ ROW *fixed_pitch_words( //find lines textord_fp_chop_error + 0.5f, &left_coutlines, &right_coutlines); - if (!left_coutlines.empty ()) - cblob_it.add_after_then_move (new C_BLOB (&left_coutlines)); - else { + if (!left_coutlines.empty()) { + cblob_it.add_after_then_move(new C_BLOB(&left_coutlines)); + } else { if (rep_left < chop_coord) { if (rep_left > prev_chop_coord) new_blanks = (uinT8) floor ((rep_left - prev_chop_coord) @@ -339,53 +339,56 @@ void fixed_split_coutline( //chop the outline //for holes C_OUTLINE_IT child_it = srcline->child (); - srcbox = srcline->bounding_box (); - //left of line - if (srcbox.left () + srcbox.right () <= chop_coord * 2 - //and not far over - && srcbox.right () < chop_coord + pitch_error) - //stick whole in left - left_it->add_after_then_move (srcline); - else if (srcbox.left () + srcbox.right () > chop_coord * 2 - && srcbox.left () > chop_coord - pitch_error) - //stick whole in right - right_it->add_before_stay_put (srcline); - else { - //needs real chopping - if (fixed_chop_coutline (srcline, chop_coord, pitch_error, - &left_frags, &right_frags)) { - for (child_it.mark_cycle_pt (); !child_it.cycled_list (); - child_it.forward ()) { - child = child_it.extract (); - srcbox = child->bounding_box (); - if (srcbox.right () < chop_coord) - left_ch_it.add_after_then_move (child); - else if (srcbox.left () > chop_coord) + srcbox = srcline->bounding_box(); + if (srcbox.left() + srcbox.right() <= chop_coord * 2 + && srcbox.right() < chop_coord + pitch_error) { + // Whole outline is in the left side or not far over the chop_coord, + // so put the whole thing on the left. + left_it->add_after_then_move(srcline); + } else if (srcbox.left() + srcbox.right() > chop_coord * 2 + && srcbox.left () > chop_coord - pitch_error) { + // Whole outline is in the right side or not far over the chop_coord, + // so put the whole thing on the right. + right_it->add_before_stay_put(srcline); + } else { + // Needs real chopping. + if (fixed_chop_coutline(srcline, chop_coord, pitch_error, + &left_frags, &right_frags)) { + for (child_it.mark_cycle_pt(); !child_it.cycled_list(); + child_it.forward()) { + child = child_it.extract(); + srcbox = child->bounding_box(); + if (srcbox.right() < chop_coord) { + // Whole child is on the left. + left_ch_it.add_after_then_move(child); + } else if (srcbox.left() > chop_coord) { + // Whole child is on the right. right_ch_it.add_after_then_move (child); - else { - if (fixed_chop_coutline (child, chop_coord, pitch_error, - &left_frags, &right_frags)) + } else { + // No pitch_error is allowed when chopping children to prevent + // impossible outlines from being created. + if (fixed_chop_coutline(child, chop_coord, 0.0f, + &left_frags, &right_frags)) { delete child; - else { - if (srcbox.left () + srcbox.right () <= chop_coord * 2) - left_ch_it.add_after_then_move (child); + } else { + if (srcbox.left() + srcbox.right() <= chop_coord * 2) + left_ch_it.add_after_then_move(child); else - right_ch_it.add_after_then_move (child); + right_ch_it.add_after_then_move(child); } } } close_chopped_cfragments(&left_frags, &left_ch, pitch_error, left_it); close_chopped_cfragments(&right_frags, &right_ch, pitch_error, right_it); - ASSERT_HOST (left_ch.empty () && right_ch.empty ()); - //no children left - delete srcline; //smashed up - } - else { - if (srcbox.left () + srcbox.right () <= chop_coord * 2) - //stick whole in left - left_it->add_after_then_move (srcline); + ASSERT_HOST(left_ch.empty() && right_ch.empty()); + // No children left. + delete srcline; // Smashed up. + } else { + // Chop failed. Just use middle coord. + if (srcbox.left() + srcbox.right() <= chop_coord * 2) + left_it->add_after_then_move(srcline); // Stick whole in left. else - right_it->add_before_stay_put (srcline); + right_it->add_before_stay_put(srcline); } } } @@ -516,253 +519,6 @@ BOOL8 fixed_chop_coutline( //chop the outline return TRUE; //did some chopping } - -/********************************************************************** - * next_anti_left_seg - * - * Search the outline for a suitable point at which it crosses the - * chop_coord from left to right. - **********************************************************************/ - -inT16 next_anti_left_seg( //chop the outline - C_OUTLINE *srcline, //source outline - inT16 tail_index, //of tailpos - inT16 startindex, //end of search - inT32 length, //of outline - inT16 chop_coord, //place to chop - float pitch_error, //allowed deviation - ICOORD *tail_pos //current position - ) { - BOOL8 test_valid; //test pt valid - inT16 chop_starty; //test chop pt - inT16 test_index; //possible chop pt - ICOORD test_pos; //possible chop pt - ICOORD prev_step; //in x to tail pos - - test_valid = FALSE; - chop_starty = -MAX_INT16; - test_index = tail_index; //stop warnings - do { - *tail_pos += srcline->step (tail_index); - prev_step = srcline->step (tail_index); - tail_index++; - if (tail_index >= length) - tail_index = 0; - if (test_valid && tail_pos->x () == chop_coord && prev_step.x () < 0) { - if (tail_pos->y () >= chop_starty) { - chop_starty = -MAX_INT16; - test_valid = FALSE; - } - else { - *tail_pos = test_pos; - tail_index = test_index; - break; //must chop there - } - } - if (tail_pos->x () == chop_coord - && srcline->step (tail_index).x () > 0 - && tail_pos->y () > chop_starty) { - chop_starty = tail_pos->y (); - test_index = tail_index; - test_pos = *tail_pos; - test_valid = TRUE; - } - else if (tail_pos->x () == chop_coord - && srcline->step (tail_index).y () < 0 - && prev_step.x () > 0 && tail_pos->y () > chop_starty) - break; //must chop here - } - while (tail_index != startindex - && tail_pos->x () < chop_coord + pitch_error); - return tail_index; -} - - -/********************************************************************** - * next_anti_right_seg - * - * Search the outline for a suitable point at which it crosses the - * chop_coord from right to left. - **********************************************************************/ - -inT16 next_anti_right_seg( //chop the outline - C_OUTLINE *srcline, //source outline - inT16 tail_index, //of tailpos - inT16 startindex, //end of search - inT32 length, //of outline - inT16 chop_coord, //place to chop - float pitch_error, //allowed deviation - ICOORD *tail_pos //current position - ) { - BOOL8 test_valid; //test pt valid - inT16 chop_starty; //test chop pt - inT16 test_index; //possible chop pt - ICOORD test_pos; //possible chop pt - ICOORD prev_step; //in x to tail pos - - test_valid = FALSE; - chop_starty = MAX_INT16; - test_index = tail_index; //stop warnings - do { - //move forward - *tail_pos += srcline->step (tail_index); - prev_step = srcline->step (tail_index); - tail_index++; - if (tail_index >= length) - tail_index = 0; - if (test_valid && tail_pos->x () == chop_coord && prev_step.x () > 0) { - if (tail_pos->y () <= chop_starty) { - chop_starty = MAX_INT16; - test_valid = FALSE; - } - else { - *tail_pos = test_pos; - tail_index = test_index; - break; //must chop there - } - } - if (tail_pos->x () == chop_coord - && srcline->step (tail_index).x () < 0 - && tail_pos->y () < chop_starty) { - chop_starty = tail_pos->y (); - test_index = tail_index; - test_pos = *tail_pos; - test_valid = TRUE; //save possible chop pt - } - else if (tail_pos->x () == chop_coord - && srcline->step (tail_index).y () > 0 - && prev_step.x () < 0 && tail_pos->y () < chop_starty) - break; //must chop here - } - while (tail_index != startindex - && tail_pos->x () > chop_coord - pitch_error); - return tail_index; -} - - -/********************************************************************** - * next_clock_left_seg - * - * Search the outline for a suitable point at which it crosses the - * chop_coord from left to right. - **********************************************************************/ - -inT16 next_clock_left_seg( //chop the outline - C_OUTLINE *srcline, //source outline - inT16 tail_index, //of tailpos - inT16 startindex, //end of search - inT32 length, //of outline - inT16 chop_coord, //place to chop - float pitch_error, //allowed deviation - ICOORD *tail_pos //current position - ) { - BOOL8 test_valid; //test pt valid - inT16 chop_starty; //test chop pt - inT16 test_index; //possible chop pt - ICOORD test_pos; //possible chop pt - ICOORD prev_step; //in x to tail pos - - test_valid = FALSE; - chop_starty = MAX_INT16; - test_index = tail_index; //stop warnings - do { - *tail_pos += srcline->step (tail_index); - prev_step = srcline->step (tail_index); - tail_index++; - if (tail_index >= length) - tail_index = 0; - if (test_valid && tail_pos->x () == chop_coord && prev_step.x () < 0) { - if (tail_pos->y () <= chop_starty) { - chop_starty = MAX_INT16; - test_valid = FALSE; - } - else { - *tail_pos = test_pos; - tail_index = test_index; - break; //must chop there - } - } - if (tail_pos->x () == chop_coord - && srcline->step (tail_index).x () > 0 - && tail_pos->y () < chop_starty) { - chop_starty = tail_pos->y (); - test_index = tail_index; - test_pos = *tail_pos; - test_valid = TRUE; - } - else if (tail_pos->x () == chop_coord - && srcline->step (tail_index).y () > 0 - && prev_step.x () > 0 && tail_pos->y () < chop_starty) - break; //must chop here - } - while (tail_index != startindex - && tail_pos->x () < chop_coord + pitch_error); - return tail_index; -} - - -/********************************************************************** - * next_clock_right_seg - * - * Search the outline for a suitable point at which it crosses the - * chop_coord from right to left. - **********************************************************************/ - -inT16 next_clock_right_seg( //chop the outline - C_OUTLINE *srcline, //source outline - inT16 tail_index, //of tailpos - inT16 startindex, //end of search - inT32 length, //of outline - inT16 chop_coord, //place to chop - float pitch_error, //allowed deviation - ICOORD *tail_pos //current position - ) { - BOOL8 test_valid; //test pt valid - inT16 chop_starty; //test chop pt - inT16 test_index; //possible chop pt - ICOORD test_pos; //possible chop pt - ICOORD prev_step; //in x to tail pos - - test_valid = FALSE; - chop_starty = MAX_INT16; - test_index = tail_index; //stop warnings - do { - //move forward - *tail_pos += srcline->step (tail_index); - prev_step = srcline->step (tail_index); - tail_index++; - if (tail_index >= length) - tail_index = 0; - if (test_valid && tail_pos->x () == chop_coord && prev_step.x () > 0) { - if (tail_pos->y () >= chop_starty) { - chop_starty = MAX_INT16; - test_valid = FALSE; - } - else { - *tail_pos = test_pos; - tail_index = test_index; - break; //must chop there - } - } - if (tail_pos->x () == chop_coord - && srcline->step (tail_index).x () < 0 - && tail_pos->y () > chop_starty) { - chop_starty = tail_pos->y (); - test_index = tail_index; - test_pos = *tail_pos; - test_valid = TRUE; //save possible chop pt - } - else if (tail_pos->x () == chop_coord - && srcline->step (tail_index).y () < 0 - && prev_step.x () < 0 && tail_pos->y () > chop_starty) - break; //must chop here - } - while (tail_index != startindex - && tail_pos->x () > chop_coord - pitch_error); - return tail_index; -} - - /********************************************************************** * save_chop_cfragment * @@ -904,36 +660,36 @@ void close_chopped_cfragments( //chop the outline C_OUTLINE_IT child_it = children; C_OUTLINE_IT olchild_it; //children of outline - while (!frag_it.empty ()) { - frag_it.move_to_first (); - //get bottom one - bottom_frag = frag_it.extract (); - frag_it.forward (); - top_frag = frag_it.data (); //look at next + while (!frag_it.empty()) { + frag_it.move_to_first(); + // get bottom one + bottom_frag = frag_it.extract(); + frag_it.forward(); + top_frag = frag_it.data(); // look at next if ((bottom_frag->steps == 0 && top_frag->steps == 0) || (bottom_frag->steps != 0 && top_frag->steps != 0)) { - if (frag_it.data_relative (1)->ycoord == top_frag->ycoord) - frag_it.forward (); + if (frag_it.data_relative(1)->ycoord == top_frag->ycoord) + frag_it.forward(); } - top_frag = frag_it.extract (); + top_frag = frag_it.extract(); if (top_frag->other_end != bottom_frag) { - outline = join_chopped_fragments (bottom_frag, top_frag); - ASSERT_HOST (outline == NULL); - } - else { - outline = join_chopped_fragments (bottom_frag, top_frag); - ASSERT_HOST (outline != NULL); - olchild_it.set_to_list (outline->child ()); - for (child_it.mark_cycle_pt (); !child_it.cycled_list (); - child_it.forward ()) { - child = child_it.data (); - if (*child < *outline) - olchild_it.add_to_end (child_it.extract ()); + outline = join_chopped_fragments(bottom_frag, top_frag); + ASSERT_HOST(outline == NULL); + } else { + outline = join_chopped_fragments(bottom_frag, top_frag); + if (outline != NULL) { + olchild_it.set_to_list(outline->child()); + for (child_it.mark_cycle_pt(); !child_it.cycled_list(); + child_it.forward()) { + child = child_it.data(); + if (*child < *outline) + olchild_it.add_to_end(child_it.extract()); + } + if (outline->bounding_box().width() > pitch_error) + dest_it->add_after_then_move(outline); + else + delete outline; // Make it disappear. } - if (outline->bounding_box ().width () > pitch_error) - dest_it->add_after_then_move (outline); - else - delete outline; //make it disappear } } while (!child_it.empty ()) { @@ -1042,6 +798,8 @@ C_OUTLINE *C_OUTLINE_FRAG::close() { //join pieces fake_step = 96; new_stepcount = stepcount + fake_count; + if (new_stepcount > C_OUTLINE::kMaxOutlineLength) + return NULL; // Can't join them new_steps = new DIR128[new_stepcount]; memmove(new_steps, steps, stepcount); memset (new_steps + stepcount, fake_step.get_dir(), fake_count); diff --git a/textord/fpchop.h b/textord/fpchop.h index b83e69e580..4e4a78bcdd 100644 --- a/textord/fpchop.h +++ b/textord/fpchop.h @@ -103,42 +103,6 @@ BOOL8 fixed_chop_coutline( //chop the outline C_OUTLINE_FRAG_LIST *left_frags, //left half of chop C_OUTLINE_FRAG_LIST *right_frags //right half of chop ); -inT16 next_anti_left_seg( //chop the outline - C_OUTLINE *srcline, //source outline - inT16 tail_index, //of tailpos - inT16 startindex, //end of search - inT32 length, //of outline - inT16 chop_coord, //place to chop - float pitch_error, //allowed deviation - ICOORD *tail_pos //current position - ); -inT16 next_anti_right_seg( //chop the outline - C_OUTLINE *srcline, //source outline - inT16 tail_index, //of tailpos - inT16 startindex, //end of search - inT32 length, //of outline - inT16 chop_coord, //place to chop - float pitch_error, //allowed deviation - ICOORD *tail_pos //current position - ); -inT16 next_clock_left_seg( //chop the outline - C_OUTLINE *srcline, //source outline - inT16 tail_index, //of tailpos - inT16 startindex, //end of search - inT32 length, //of outline - inT16 chop_coord, //place to chop - float pitch_error, //allowed deviation - ICOORD *tail_pos //current position - ); -inT16 next_clock_right_seg( //chop the outline - C_OUTLINE *srcline, //source outline - inT16 tail_index, //of tailpos - inT16 startindex, //end of search - inT32 length, //of outline - inT16 chop_coord, //place to chop - float pitch_error, //allowed deviation - ICOORD *tail_pos //current position - ); void save_chop_cfragment( //chop the outline inT16 head_index, //head of fragment ICOORD head_pos, //head of fragment diff --git a/textord/gap_map.cpp b/textord/gap_map.cpp index 943fa000e7..2f8440e601 100644 --- a/textord/gap_map.cpp +++ b/textord/gap_map.cpp @@ -86,7 +86,8 @@ GAPMAP::GAPMAP( //Constructor if ((gap_width > gapmap_big_gaps * row->xheight) && gap_width > 2) { max_quantum = (blob_box.left () - min_left) / bucket_size; - for (i = 0; i <= max_quantum; i++) + if (max_quantum > map_max) max_quantum = map_max; + for (i = 0; i <= max_quantum; i++) map[i]++; } } @@ -98,6 +99,7 @@ GAPMAP::GAPMAP( //Constructor min_quantum = (prev_blob_box.right () - min_left) / bucket_size; max_quantum = (blob_box.left () - min_left) / bucket_size; + if (max_quantum > map_max) max_quantum = map_max; for (i = min_quantum; i <= max_quantum; i++) map[i]++; } @@ -110,6 +112,7 @@ GAPMAP::GAPMAP( //Constructor && gap_width > 2) { min_quantum = (prev_blob_box.right () - min_left) / bucket_size; + if (min_quantum < 0) min_quantum = 0; for (i = min_quantum; i <= map_max; i++) map[i]++; } @@ -158,6 +161,11 @@ BOOL8 GAPMAP::table_gap( //Is gap a table? min_quantum = (left - min_left) / bucket_size; max_quantum = (right - min_left) / bucket_size; + // Clip to the bounds of the array. In some circumstances (big blob followed + // by small blob) max_quantum can exceed the map_max bounds, but we clip + // here instead, as it provides better long-term safety. + if (min_quantum < 0) min_quantum = 0; + if (max_quantum > map_max) max_quantum = map_max; for (i = min_quantum; (!tab_found && (i <= max_quantum)); i++) if (map[i] > total_rows / 2) tab_found = TRUE; diff --git a/textord/linefind.cpp b/textord/linefind.cpp index 58ddbede3a..ec0d2e8549 100644 --- a/textord/linefind.cpp +++ b/textord/linefind.cpp @@ -69,7 +69,7 @@ static void RemoveUnusedLineSegments(bool horizontal_lines, BLOBNBOX_IT bbox_it(line_bblobs); for (bbox_it.mark_cycle_pt(); !bbox_it.cycled_list(); bbox_it.forward()) { BLOBNBOX* blob = bbox_it.data(); - if (blob->left_tab_type() == TT_MAYBE_ALIGNED) { + if (blob->left_tab_type() != TT_VLINE) { const TBOX& box = blob->bounding_box(); Box* pixbox = NULL; if (horizontal_lines) { @@ -79,7 +79,6 @@ static void RemoveUnusedLineSegments(bool horizontal_lines, // See GetLineBoxes for more explanation. pixbox = boxCreate(box.bottom(), height - box.right(), box.height(), box.width()); - } else { // For vertical lines, just flip upside-down to convert to Leptonica. // The y position of the box in Leptonica terms is the distance from diff --git a/textord/makerow.cpp b/textord/makerow.cpp index abcff3d4f0..67530b2379 100644 --- a/textord/makerow.cpp +++ b/textord/makerow.cpp @@ -57,10 +57,10 @@ BOOL_VAR(textord_fix_makerow_bug, TRUE, "Prevent multiple baselines"); BOOL_VAR(textord_debug_xheights, FALSE, "Test xheight algorithms"); BOOL_VAR(textord_biased_skewcalc, TRUE, "Bias skew estimates with line length"); BOOL_VAR(textord_interpolating_skew, TRUE, "Interpolate across gaps"); -INT_VAR(textord_skewsmooth_offset, 2, "For smooth factor"); +INT_VAR(textord_skewsmooth_offset, 4, "For smooth factor"); INT_VAR(textord_skewsmooth_offset2, 1, "For smooth factor"); -INT_VAR(textord_test_x, -1, "coord of test pt"); -INT_VAR(textord_test_y, -1, "coord of test pt"); +INT_VAR(textord_test_x, -MAX_INT32, "coord of test pt"); +INT_VAR(textord_test_y, -MAX_INT32, "coord of test pt"); INT_VAR(textord_min_blobs_in_row, 4, "Min blobs before gradient counted"); INT_VAR(textord_spline_minblobs, 8, "Min blobs in each spline segment"); INT_VAR(textord_spline_medianwin, 6, "Size of window for spline segmentation"); @@ -72,13 +72,13 @@ double_VAR(textord_spline_shift_fraction, 0.02, double_VAR(textord_spline_outlier_fraction, 0.1, "Fraction of line spacing for outlier"); double_VAR(textord_skew_ile, 0.5, "Ile of gradients for page skew"); -double_VAR(textord_skew_lag, 0.01, "Lag for skew on row accumulation"); +double_VAR(textord_skew_lag, 0.02, "Lag for skew on row accumulation"); double_VAR(textord_linespace_iqrlimit, 0.2, "Max iqr/median for linespace"); double_VAR(textord_width_limit, 8, "Max width of blobs to make rows"); double_VAR(textord_chop_width, 1.5, "Max width before chopping"); double_VAR(textord_expansion_factor, 1.0, "Factor to expand rows by in expand_rows"); -double_VAR(textord_overlap_x, 0.5, "Fraction of linespace for good overlap"); +double_VAR(textord_overlap_x, 0.375, "Fraction of linespace for good overlap"); double_VAR(textord_minxh, 0.25, "fraction of linesize for min xheight"); double_VAR(textord_min_linesize, 1.25, "* blob height for initial linesize"); double_VAR(textord_excess_blobsize, 1.3, @@ -143,12 +143,9 @@ float MakeRowFromSubBlobs(TO_BLOCK* block, C_BLOB* blob, TO_ROW_IT* row_it) { return 0.0f; for (ol_it.mark_cycle_pt(); !ol_it.cycled_list(); ol_it.forward()) { // Deep copy the child outline and use that to make a blob. - C_OUTLINE* outline = C_OUTLINE::deep_copy(ol_it.data()); - // The constructor from a list of outlines corrects the direction. - C_OUTLINE_LIST outlines; - C_OUTLINE_IT ol_it(&outlines); - ol_it.add_after_then_move(outline); - C_BLOB* blob = new C_BLOB(&outlines); + C_BLOB* blob = new C_BLOB(C_OUTLINE::deep_copy(ol_it.data())); + // Correct direction as needed. + blob->CheckInverseFlagAndDirection(); BLOBNBOX* bbox = new BLOBNBOX(blob); bb_it.add_after_then_move(bbox); } @@ -214,19 +211,6 @@ float make_rows(ICOORD page_tr, TO_BLOCK_LIST *port_blocks) { return port_m; // global skew } -namespace tesseract { - -void Textord::fit_rows(float gradient, ICOORD page_tr, TO_BLOCK_LIST *blocks) { - TO_BLOCK_IT block_it(blocks); // iterator - for (block_it.mark_cycle_pt(); !block_it.cycled_list(); block_it.forward()) { - cleanup_rows_fitting(page_tr, block_it.data(), gradient, FCOORD(1.0f, 0.0f), - block_it.data()->block->bounding_box().left(), - !(BOOL8)textord_test_landscape); - } -} - -} // namespace tesseract. - /** * @name make_initial_textrows * @@ -478,7 +462,7 @@ static bool dot_of_i(BLOBNBOX* dot, BLOBNBOX* i, TO_ROW* row) { return false; } -static void vigorous_noise_removal(TO_BLOCK* block) { +void vigorous_noise_removal(TO_BLOCK* block) { TO_ROW_IT row_it = block->get_rows (); for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { TO_ROW* row = row_it.data(); @@ -578,86 +562,6 @@ void cleanup_rows_making( //find lines assign_blobs_to_rows (block, &gradient, 3, FALSE, FALSE, FALSE); } -namespace tesseract { - -void Textord::cleanup_rows_fitting(ICOORD page_tr, // top right - TO_BLOCK *block, // block to do - float gradient, // gradient to fit - FCOORD rotation, // for drawing - inT32 block_edge, // edge of block - BOOL8 testing_on) { // correct orientation - BLOBNBOX_IT blob_it = &block->blobs; - TO_ROW_IT row_it = block->get_rows(); - -#ifndef GRAPHICS_DISABLED - if (textord_show_parallel_rows && testing_on) { - if (to_win == NULL) - create_to_win(page_tr); - } -#endif - for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) - row_it.data()->blob_list()->sort(blob_x_order); - fit_parallel_rows(block, gradient, rotation, block_edge, FALSE); - if (textord_heavy_nr) { - vigorous_noise_removal(block); - } - POLY_BLOCK* pb = block->block->poly_block(); - if (pb == NULL || pb->IsText()) { - separate_underlines(block, gradient, rotation, testing_on); - pre_associate_blobs(page_tr, block, rotation, testing_on); - } - -#ifndef GRAPHICS_DISABLED - if (textord_show_final_rows && testing_on) { - if (to_win == NULL) - create_to_win(page_tr); - } -#endif - - fit_parallel_rows(block, gradient, rotation, block_edge, FALSE); - // textord_show_final_rows && testing_on); - make_spline_rows(block, - gradient, - rotation, - block_edge, - textord_show_final_rows && testing_on); - // We only want to call compute_block_xheight() if - // both textord_old_xheight and textord_old_baselines are false. - // No need to call compute_block_xheight() if textord_old_baselines - // is true, since all appropriate xheight computation functions - // would be called from make_old_baselines(). - // Note: it can not be the case that textord_old_baselines is - // false, and textord_old_xheight is true. - if (!textord_old_xheight && !textord_old_baselines) - compute_block_xheight(block, gradient); - if (textord_restore_underlines) // fix underlines - restore_underlined_blobs(block); -#ifndef GRAPHICS_DISABLED - if (textord_show_final_rows && testing_on) { - ScrollView::Color colour = ScrollView::RED; - for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) { - plot_parallel_row(row_it.data(), gradient, - block_edge, colour, rotation); - colour = (ScrollView::Color) (colour + 1); - if (colour > ScrollView::MAGENTA) - colour = ScrollView::RED; - } - plot_blob_list(to_win, &block->blobs, - ScrollView::MAGENTA, ScrollView::WHITE); - //show discarded blobs - plot_blob_list(to_win, &block->underlines, - ScrollView::YELLOW, ScrollView::CORAL); - } - if (textord_show_final_rows && testing_on && block->blobs.length () > 0) - tprintf ("%d blobs discarded as noise\n", block->blobs.length ()); - if (textord_show_final_rows && testing_on) { - draw_meanlines(block, gradient, block_edge, ScrollView::WHITE, rotation); - } -#endif -} - -} // namespace tesseract. - /** * delete_non_dropout_rows * @@ -2121,8 +2025,6 @@ void fit_parallel_lms(float gradient, TO_ROW *row) { namespace tesseract { void Textord::make_spline_rows(TO_BLOCK *block, // block to do float gradient, // gradient to fit - FCOORD rotation, // for drawing - inT32 block_edge, // edge of block BOOL8 testing_on) { #ifndef GRAPHICS_DISABLED ScrollView::Color colour; //of row @@ -2544,47 +2446,51 @@ void assign_blobs_to_rows( //find lines else if (make_new_rows && top - bottom < block->max_blob_size) { overlap_result = NEW_ROW; dest_row = - new TO_ROW (blob_it.extract (), top, bottom, block->line_size); + new TO_ROW(blob_it.extract(), top, bottom, block->line_size); row_count++; - row_it.add_after_then_move (dest_row); + row_it.add_after_then_move(dest_row); smooth_factor = 1.0 / (row_count * textord_skew_lag + textord_skewsmooth_offset2); } else overlap_result = REJECT; - if (blob->bounding_box ().contains (testpt)) { + if (blob->bounding_box ().contains(testpt)) { if (overlap_result != REJECT) { - tprintf ("Test blob assigned to row at (%g,%g) on pass %d\n", - dest_row->min_y (), dest_row->max_y (), pass); + tprintf("Test blob assigned to row at (%g,%g) on pass %d\n", + dest_row->min_y(), dest_row->max_y(), pass); } else { - tprintf ("Test blob assigned to no row on pass %d\n", pass); + tprintf("Test blob assigned to no row on pass %d\n", pass); } } if (overlap_result != REJECT) { - while (!row_it.at_first () - && row_it.data ()->min_y () > - row_it.data_relative (-1)->min_y ()) { - row = row_it.extract (); - row_it.backward (); - row_it.add_before_then_move (row); + while (!row_it.at_first() && + row_it.data()->min_y() > row_it.data_relative(-1)->min_y()) { + row = row_it.extract(); + row_it.backward(); + row_it.add_before_then_move(row); + } + while (!row_it.at_last() && + row_it.data ()->min_y() < row_it.data_relative (1)->min_y()) { + row = row_it.extract(); + row_it.forward(); + // Keep rows in order. + row_it.add_after_then_move(row); } - while (!row_it.at_last () - && row_it.data ()->min_y () < - row_it.data_relative (1)->min_y ()) { - row = row_it.extract (); - row_it.forward (); - //keep rows in order - row_it.add_after_then_move (row); + BLOBNBOX_IT added_blob_it(dest_row->blob_list()); + added_blob_it.move_to_last(); + TBOX prev_box = added_blob_it.data_relative(-1)->bounding_box(); + if (dest_row->blob_list()->singleton() || + !prev_box.major_x_overlap(blob->bounding_box())) { + block_skew = (1 - smooth_factor) * block_skew + + smooth_factor * (blob->bounding_box().bottom() - + dest_row->initial_min_y()); } - block_skew = (1 - smooth_factor) * block_skew - + smooth_factor * (blob->bounding_box ().bottom () - - dest_row->initial_min_y ()); } } - for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { - if (row_it.data ()->blob_list ()->empty ()) - delete row_it.extract (); //discard empty rows + for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) { + if (row_it.data()->blob_list()->empty()) + delete row_it.extract(); // Discard empty rows. } } @@ -2620,8 +2526,8 @@ OVERLAP_STATE most_overlapping_row( //find best row //compute overlap bestover -= row->min_y () - bottom; if (testing_blob) { - tprintf ("Test blob y=(%g,%g), row=(%f,%f), overlap=%f\n", - bottom, top, row->min_y (), row->max_y (), bestover); + tprintf("Test blob y=(%g,%g), row=(%f,%f), size=%g, overlap=%f\n", + bottom, top, row->min_y(), row->max_y(), rowsize, bestover); } test_row = row; do { @@ -2663,10 +2569,9 @@ OVERLAP_STATE most_overlapping_row( //find best row row = test_row; } if (testing_blob) { - tprintf - ("Test blob y=(%g,%g), row=(%f,%f), overlap=%f->%f\n", - bottom, top, test_row->min_y (), test_row->max_y (), - overlap, bestover); + tprintf("Test blob y=(%g,%g), row=(%f,%f), size=%g, overlap=%f->%f\n", + bottom, top, test_row->min_y(), test_row->max_y(), + rowsize, overlap, bestover); } } } @@ -2771,9 +2676,6 @@ void mark_repeated_chars(TO_ROW *row) { if (bblob->flow() != BTFT_LEADER) break; if (bblob->joined_to_prev() || bblob->cblob() == NULL) { - tprintf("Cancelled repeat of length %d due to %s\n", - repeat_length, - bblob->joined_to_prev() ? "Joined" : "Null"); repeat_length = 0; break; } diff --git a/textord/makerow.h b/textord/makerow.h index f7feba9205..5ab173a45b 100644 --- a/textord/makerow.h +++ b/textord/makerow.h @@ -40,6 +40,7 @@ enum ROW_CATEGORY { ROW_INVALID, }; +extern BOOL_VAR_H(textord_heavy_nr, FALSE, "Vigorously remove noise"); extern BOOL_VAR_H (textord_show_initial_rows, FALSE, "Display row accumulation"); extern BOOL_VAR_H (textord_show_parallel_rows, FALSE, @@ -65,8 +66,8 @@ extern BOOL_VAR_H (textord_fix_makerow_bug, TRUE, extern BOOL_VAR_H (textord_cblob_blockocc, TRUE, "Use new projection for underlines"); extern BOOL_VAR_H (textord_debug_xheights, FALSE, "Test xheight algorithms"); -extern INT_VAR_H (textord_test_x, 0, "coord of test pt"); -extern INT_VAR_H (textord_test_y, 0, "coord of test pt"); +extern INT_VAR_H (textord_test_x, -MAX_INT32, "coord of test pt"); +extern INT_VAR_H (textord_test_y, -MAX_INT32, "coord of test pt"); extern INT_VAR_H (textord_min_blobs_in_row, 4, "Min blobs before gradient counted"); extern INT_VAR_H (textord_spline_minblobs, 8, @@ -143,6 +144,7 @@ void fit_lms_line(TO_ROW *row); void compute_page_skew(TO_BLOCK_LIST *blocks, // list of blocks float &page_m, // average gradient float &page_err); // average error +void vigorous_noise_removal(TO_BLOCK* block); void cleanup_rows_making(ICOORD page_tr, // top right TO_BLOCK *block, // block to do float gradient, // gradient to fit diff --git a/textord/textord.cpp b/textord/textord.cpp index 66b055f19d..dd11bc52c9 100644 --- a/textord/textord.cpp +++ b/textord/textord.cpp @@ -22,6 +22,7 @@ #include "config_auto.h" #endif +#include "baselinedetect.h" #include "drawtord.h" #include "textord.h" #include "makerow.h" @@ -206,6 +207,8 @@ Textord::Textord(CCStruct* ccstruct) ccstruct_->params()), INT_MEMBER(textord_max_noise_size, 7, "Pixel size of noise", ccstruct_->params()), + INT_MEMBER(textord_baseline_debug, 0, "Baseline debug level", + ccstruct_->params()), double_MEMBER(textord_blob_size_bigile, 95, "Percentile for large blobs", ccstruct_->params()), double_MEMBER(textord_noise_area_ratio, 0.7, @@ -262,14 +265,24 @@ Textord::~Textord() { } // Make the textlines and words inside each block. -void Textord::TextordPage(PageSegMode pageseg_mode, - int width, int height, Pix* pix, +void Textord::TextordPage(PageSegMode pageseg_mode, const FCOORD& reskew, + int width, int height, Pix* binary_pix, + Pix* thresholds_pix, Pix* grey_pix, + bool use_box_bottoms, BLOCK_LIST* blocks, TO_BLOCK_LIST* to_blocks) { page_tr_.set_x(width); page_tr_.set_y(height); if (to_blocks->empty()) { // AutoPageSeg was not used, so we need to find_components first. - find_components(pix, blocks, to_blocks); + find_components(binary_pix, blocks, to_blocks); + TO_BLOCK_IT it(to_blocks); + for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { + TO_BLOCK* to_block = it.data(); + // Compute the edge offsets whether or not there is a grey_pix. + // We have by-passed auto page seg, so we have to run it here. + // By page segmentation mode there is no non-text to avoid running on. + to_block->ComputeEdgeOffsets(thresholds_pix, grey_pix); + } } else if (!PSM_SPARSE(pageseg_mode)) { // AutoPageSeg does not need to find_components as it did that already. // Filter_blobs sets up the TO_BLOCKs the same as find_components does. @@ -307,8 +320,13 @@ void Textord::TextordPage(PageSegMode pageseg_mode, // SINGLE_LINE, SINGLE_WORD and SINGLE_CHAR all need a single row. gradient = make_single_row(page_tr_, to_block, to_blocks); } - // Now fit baselines. For now only old mode is available. - fit_rows(gradient, page_tr_, to_blocks); + BaselineDetect baseline_detector(textord_baseline_debug, + reskew, to_blocks); + baseline_detector.ComputeStraightBaselines(use_box_bottoms); + baseline_detector.ComputeBaselineSplinesAndXheights(page_tr_, true, + textord_heavy_nr, + textord_show_final_rows, + this); // Now make the words in the lines. if (PSM_WORD_FIND_ENABLED(pageseg_mode)) { // SINGLE_LINE uses the old word maker on the single line. diff --git a/textord/textord.h b/textord/textord.h index 0e8fd3a408..2b2b881de4 100644 --- a/textord/textord.h +++ b/textord/textord.h @@ -41,8 +41,16 @@ class Textord { ~Textord(); // Make the textlines and words inside each block. - void TextordPage(PageSegMode pageseg_mode, - int width, int height, Pix* pix, + // binary_pix is mandatory and is the binarized input after line removal. + // grey_pix is optional, but if present must match the binary_pix in size, + // and must be a *real* grey image instead of binary_pix * 255. + // thresholds_pix is expected to be present iff grey_pix is present and + // can be an integer factor reduction of the grey_pix. It represents the + // thresholds that were used to create the binary_pix from the grey_pix. + void TextordPage(PageSegMode pageseg_mode, const FCOORD& reskew, + int width, int height, Pix* binary_pix, + Pix* thresholds_pix, Pix* grey_pix, + bool use_box_bottoms, BLOCK_LIST* blocks, TO_BLOCK_LIST* to_blocks); // If we were supposed to return only a single textline, and there is more @@ -89,13 +97,7 @@ class Textord { const FCOORD& skew, TO_BLOCK* block, ScrollView* win); - void fit_rows(float gradient, ICOORD page_tr, TO_BLOCK_LIST *blocks); - void cleanup_rows_fitting(ICOORD page_tr, // top right - TO_BLOCK *block, // block to do - float gradient, // gradient to fit - FCOORD rotation, // for drawing - inT32 block_edge, // edge of block - BOOL8 testing_on); // correct orientation + public: void compute_block_xheight(TO_BLOCK *block, float gradient); void compute_row_xheight(TO_ROW *row, // row to do const FCOORD& rotation, @@ -103,10 +105,8 @@ class Textord { int block_line_size); void make_spline_rows(TO_BLOCK *block, // block to do float gradient, // gradient to fit - FCOORD rotation, // for drawing - inT32 block_edge, // edge of block BOOL8 testing_on); - + private: //// oldbasel.cpp //////////////////////////////////////// void make_old_baselines(TO_BLOCK *block, // block to do BOOL8 testing_on, // correct orientation @@ -201,6 +201,11 @@ class Textord { BLOBNBOX_LIST *noise_list, BLOBNBOX_LIST *small_list, BLOBNBOX_LIST *large_list); + // Fixes the block so it obeys all the rules: + // Must have at least one ROW. + // Must have at least one WERD. + // WERDs contain a fake blob. + void cleanup_nontext_block(BLOCK* block); void cleanup_blocks(BLOCK_LIST *blocks); BOOL8 clean_noise_from_row(ROW *row); void clean_noise_from_words(ROW *row); @@ -326,6 +331,7 @@ class Textord { BOOL_VAR_H(textord_show_blobs, false, "Display unsorted blobs"); BOOL_VAR_H(textord_show_boxes, false, "Display boxes"); INT_VAR_H(textord_max_noise_size, 7, "Pixel size of noise"); + INT_VAR_H(textord_baseline_debug, 0, "Baseline debug level"); double_VAR_H(textord_blob_size_bigile, 95, "Percentile for large blobs"); double_VAR_H(textord_noise_area_ratio, 0.7, "Fraction of bounding box for noise"); diff --git a/textord/tordmain.cpp b/textord/tordmain.cpp index 45cab46ace..3ca31cc91d 100644 --- a/textord/tordmain.cpp +++ b/textord/tordmain.cpp @@ -305,18 +305,18 @@ float Textord::filter_noise_blobs( float max_x; float max_height; //of good blobs - for (src_it.mark_cycle_pt (); !src_it.cycled_list (); src_it.forward ()) { - blob = src_it.data (); - if (blob->bounding_box ().height () < textord_max_noise_size) - noise_it.add_after_then_move (src_it.extract ()); - else if (blob->enclosed_area () >= blob->bounding_box ().height () - * blob->bounding_box ().width () * textord_noise_area_ratio) - small_it.add_after_then_move (src_it.extract ()); + for (src_it.mark_cycle_pt(); !src_it.cycled_list(); src_it.forward()) { + blob = src_it.data(); + if (blob->bounding_box().height() < textord_max_noise_size) + noise_it.add_after_then_move(src_it.extract()); + else if (blob->enclosed_area() >= blob->bounding_box().height() + * blob->bounding_box().width() * textord_noise_area_ratio) + small_it.add_after_then_move(src_it.extract()); } - for (src_it.mark_cycle_pt (); !src_it.cycled_list (); src_it.forward ()) { - size_stats.add (src_it.data ()->bounding_box ().height (), 1); + for (src_it.mark_cycle_pt(); !src_it.cycled_list(); src_it.forward()) { + size_stats.add(src_it.data()->bounding_box().height(), 1); } - initial_x = size_stats.ile (textord_initialx_ile); + initial_x = size_stats.ile(textord_initialx_ile); max_y = ceil(initial_x * (tesseract::CCStruct::kDescenderFraction + tesseract::CCStruct::kXHeightFraction + @@ -354,6 +354,46 @@ float Textord::filter_noise_blobs( return initial_x; } +// Fixes the block so it obeys all the rules: +// Must have at least one ROW. +// Must have at least one WERD. +// WERDs contain a fake blob. +void Textord::cleanup_nontext_block(BLOCK* block) { + // Non-text blocks must contain at least one row. + ROW_IT row_it(block->row_list()); + if (row_it.empty()) { + float height = block->bounding_box().height(); + inT32 zero = 0; + ROW* row = new ROW(0, &zero, NULL, height / 2.0f, height / 4.0f, + height / 4.0f, 0, 1); + row_it.add_after_then_move(row); + } + // Each row must contain at least one word. + for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) { + ROW* row = row_it.data(); + WERD_IT w_it(row->word_list()); + if (w_it.empty()) { + // Make a fake blob to put in the word. + TBOX box = block->row_list()->singleton() ? block->bounding_box() + : row->bounding_box(); + C_BLOB* blob = C_BLOB::FakeBlob(box); + C_BLOB_LIST blobs; + C_BLOB_IT blob_it(&blobs); + blob_it.add_after_then_move(blob); + WERD* word = new WERD(&blobs, 0, NULL); + w_it.add_after_then_move(word); + } + // Each word must contain a fake blob. + for (w_it.mark_cycle_pt(); !w_it.cycled_list(); w_it.forward()) { + WERD* word = w_it.data(); + // Just assert that this is true, as it would be useful to find + // out why it isn't. + ASSERT_HOST(!word->cblob_list()->empty()); + } + row->recalc_bounding_box(); + } +} + /********************************************************************** * cleanup_blocks * @@ -370,22 +410,26 @@ void Textord::cleanup_blocks( //remove empties int num_rows_all = 0; int num_blocks = 0; int num_blocks_all = 0; - for (block_it.mark_cycle_pt (); !block_it.cycled_list (); - block_it.forward ()) { + for (block_it.mark_cycle_pt(); !block_it.cycled_list(); + block_it.forward()) { + BLOCK* block = block_it.data(); + if (block->poly_block() != NULL && !block->poly_block()->IsText()) { + cleanup_nontext_block(block); + continue; + } num_rows = 0; num_rows_all = 0; - row_it.set_to_list (block_it.data ()->row_list ()); - for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) { + row_it.set_to_list(block->row_list()); + for (row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward()) { ++num_rows_all; clean_small_noise_from_words(row_it.data()); - if ((textord_noise_rejrows - && !row_it.data ()->word_list ()->empty () - && clean_noise_from_row (row_it.data ())) - || row_it.data ()->word_list ()->empty ()) - delete row_it.extract ();//lose empty row - else { + if ((textord_noise_rejrows && !row_it.data()->word_list()->empty() && + clean_noise_from_row(row_it.data())) || + row_it.data()->word_list()->empty()) { + delete row_it.extract(); // lose empty row. + } else { if (textord_noise_rejwords) - clean_noise_from_words (row_it.data ()); + clean_noise_from_words(row_it.data()); if (textord_blshift_maxshift >= 0) tweak_row_baseline(row_it.data(), textord_blshift_maxshift, @@ -393,10 +437,8 @@ void Textord::cleanup_blocks( //remove empties ++num_rows; } } - if (block_it.data()->row_list()->empty() && - (block_it.data()->poly_block() == NULL || - block_it.data()->poly_block()->IsText())) { - delete block_it.extract(); // Lose empty text blocks but not other types. + if (block->row_list()->empty()) { + delete block_it.extract(); // Lose empty text blocks. } else { ++num_blocks; } diff --git a/textord/tospace.cpp b/textord/tospace.cpp index 837aa689c3..f09038d633 100644 --- a/textord/tospace.cpp +++ b/textord/tospace.cpp @@ -16,12 +16,11 @@ * **********************************************************************/ -#include "textord.h" -#include "tovars.h" #include "drawtord.h" -#include "textord.h" #include "ndminx.h" #include "statistc.h" +#include "textord.h" +#include "tovars.h" // Include automatically generated configuration file if running autoconf. #ifdef HAVE_CONFIG_H diff --git a/textord/underlin.cpp b/textord/underlin.cpp index 4d3a367e98..cf488e91f6 100644 --- a/textord/underlin.cpp +++ b/textord/underlin.cpp @@ -81,13 +81,6 @@ void restore_underlined_blobs( //get chop points &left_coutlines, &right_coutlines); if (!left_coutlines.empty()) { row->insert_blob(new BLOBNBOX(new C_BLOB(&left_coutlines))); - } else { - fprintf(stderr, - "Error:no outlines after chopping from %d to %d from (%d,%d)->(%d,%d)\n", - cell_it.data ()->x (), cell_it.data ()->y (), - blob_box.left (), blob_box.bottom (), - blob_box.right (), blob_box.top ()); - ASSERT_HOST(FALSE); } u_line = NULL; //no more blobs to add } diff --git a/textord/workingpartset.cpp b/textord/workingpartset.cpp index 6472e79bd7..0257154932 100644 --- a/textord/workingpartset.cpp +++ b/textord/workingpartset.cpp @@ -92,7 +92,8 @@ void WorkingPartSet::MakeBlocks(const ICOORD& bleft, const ICOORD& tright, bool text_block = false; do { ColPartition* part = part_it_.extract(); - if (part->blob_type() == BRT_UNKNOWN || part->blob_type() == BRT_TEXT) + if (part->blob_type() == BRT_UNKNOWN || + (part->IsTextType() && part->type() != PT_TABLE)) text_block = true; part->set_working_set(NULL); part_it_.forward(); @@ -113,9 +114,9 @@ void WorkingPartSet::MakeBlocks(const ICOORD& bleft, const ICOORD& tright, // current box, nor (if image) too far below. PolyBlockType type = part->type(), next_type = next_block_part->type(); if (ColPartition::TypesSimilar(type, next_type) && + !part->IsLineType() && !next_block_part->IsLineType() && next_box.bottom() <= part_box.top() && - (text_block || - part_box.bottom() - next_box.top() < part_box.height())) + (text_block || part_box.bottom() <= next_box.top())) next_part = next_block_part; } } while (!part_it_.empty() && next_part != NULL); diff --git a/training/Makefile.am b/training/Makefile.am index 6c08096ac0..bac7687c26 100644 --- a/training/Makefile.am +++ b/training/Makefile.am @@ -14,17 +14,20 @@ AM_LDFLAGS += -all-static endif noinst_HEADERS = \ - commontraining.h tessopt.h mergenf.h + commandlineflags.h commontraining.h fileio.h icuerrorcode.h normstrngs.h \ + tessopt.h mergenf.h noinst_LTLIBRARIES = libtesseract_training.la libtesseract_tessopt.la libtesseract_training_la_SOURCES = \ - commontraining.cpp + commandlineflags.cpp commontraining.cpp fileio.cpp normstrngs.cpp libtesseract_tessopt_la_SOURCES = \ tessopt.cpp -bin_PROGRAMS = ambiguous_words classifier_tester cntraining combine_tessdata dawg2wordlist mftraining shapeclustering unicharset_extractor wordlist2dawg +bin_PROGRAMS = ambiguous_words classifier_tester cntraining combine_tessdata \ + dawg2wordlist mftraining set_unicharset_properties shapeclustering \ + unicharset_extractor wordlist2dawg ambiguous_words_SOURCES = ambiguous_words.cpp ambiguous_words_LDADD = \ @@ -156,6 +159,32 @@ mftraining_LDADD += \ ../api/libtesseract.la endif +set_unicharset_properties_SOURCES = set_unicharset_properties.cpp +#set_unicharset_properties_LDFLAGS = -static +set_unicharset_properties_LDADD = \ + libtesseract_training.la \ + libtesseract_tessopt.la \ + -licuuc +if USING_MULTIPLELIBS +set_unicharset_properties_LDADD += \ + ../textord/libtesseract_textord.la \ + ../classify/libtesseract_classify.la \ + ../dict/libtesseract_dict.la \ + ../ccstruct/libtesseract_ccstruct.la \ + ../image/libtesseract_image.la \ + ../cutil/libtesseract_cutil.la \ + ../viewer/libtesseract_viewer.la \ + ../ccmain/libtesseract_main.la \ + ../cube/libtesseract_cube.la \ + ../neural_networks/runtime/libtesseract_neural.la \ + ../wordrec/libtesseract_wordrec.la \ + ../ccutil/libtesseract_ccutil.la \ + -licuuc +else +set_unicharset_properties_LDADD += \ + ../api/libtesseract.la +endif + shapeclustering_SOURCES = shapeclustering.cpp #shapeclustering_LDFLAGS = -static shapeclustering_LDADD = \ @@ -226,4 +255,4 @@ mftraining_LDADD += -lws2_32 shapeclustering_LDADD += -lws2_32 unicharset_extractor_LDADD += -lws2_32 wordlist2dawg_LDADD += -lws2_32 -endif \ No newline at end of file +endif diff --git a/training/ambiguous_words.cpp b/training/ambiguous_words.cpp index 7f87b37343..2fee5e8d0c 100644 --- a/training/ambiguous_words.cpp +++ b/training/ambiguous_words.cpp @@ -70,7 +70,7 @@ int main(int argc, char** argv) { while (fgets(str, CHARS_PER_LINE, input_file) != NULL) { chomp_string(str); // remove newline WERD_CHOICE word(str, dict.getUnicharset()); - dict.NoDangerousAmbig(&word, NULL, false, NULL, NULL); + dict.NoDangerousAmbig(&word, NULL, false, NULL); } // Clean up. fclose(input_file); diff --git a/training/classifier_tester.cpp b/training/classifier_tester.cpp index 65b0922bca..8dc4792944 100644 --- a/training/classifier_tester.cpp +++ b/training/classifier_tester.cpp @@ -16,6 +16,7 @@ // but doesn't have to be the same as the training data. // Author: Ray Smith +#include #ifndef USE_STD_NAMESPACE #include "base/commandlineflags.h" #endif @@ -30,6 +31,8 @@ STRING_PARAM_FLAG(classifier, "", "Classifier to test"); STRING_PARAM_FLAG(lang, "eng", "Language to test"); STRING_PARAM_FLAG(tessdata_dir, "", "Directory of traineddata files"); +DECLARE_INT_PARAM_FLAG(debug_level); +DECLARE_STRING_PARAM_FLAG(T); enum ClassifierName { CN_PRUNER, @@ -41,6 +44,70 @@ enum ClassifierName { const char* names[] = {"pruner", "full", "cube", "cubetess", NULL }; +static tesseract::ShapeClassifier* InitializeClassifier( + const char* classifer_name, const UNICHARSET& unicharset, + int argc, char **argv, + tesseract::TessBaseAPI** api) { + // Decode the classifier string. + ClassifierName classifier = CN_COUNT; + for (int c = 0; c < CN_COUNT; ++c) { + if (strcmp(classifer_name, names[c]) == 0) { + classifier = static_cast(c); + break; + } + } + if (classifier == CN_COUNT) { + fprintf(stderr, "Invalid classifier name:%s\n", FLAGS_classifier.c_str()); + return NULL; + } + + // We need to initialize tesseract to test. + *api = new tesseract::TessBaseAPI; + tesseract::OcrEngineMode engine_mode = tesseract::OEM_TESSERACT_ONLY; + if (classifier == CN_CUBE || classifier == CN_CUBETESS) + engine_mode = tesseract::OEM_TESSERACT_CUBE_COMBINED; + tesseract::Tesseract* tesseract = NULL; + tesseract::Classify* classify = NULL; + if (classifier == CN_CUBE || classifier == CN_CUBETESS || + classifier == CN_PRUNER || classifier == CN_FULL) { + (*api)->SetVariable("cube_debug_level", "2"); + if ((*api)->Init(FLAGS_tessdata_dir.c_str(), FLAGS_lang.c_str(), + engine_mode) < 0) { + fprintf(stderr, "Tesseract initialization failed!\n"); + return NULL; + } + tesseract = const_cast((*api)->tesseract()); + classify = reinterpret_cast(tesseract); + if (classify->shape_table() == NULL) { + fprintf(stderr, "Tesseract must contain a ShapeTable!\n"); + return NULL; + } + } + tesseract::ShapeClassifier* shape_classifier = NULL; + + if (!FLAGS_T.empty()) { + const char* config_name; + while ((config_name = GetNextFilename(argc, argv)) != NULL) { + tprintf("Reading config file %s ...\n", config_name); + (*api)->ReadConfigFile(config_name); + } + } + if (classifier == CN_PRUNER) { + shape_classifier = new tesseract::TessClassifier(true, classify); + } else if (classifier == CN_FULL) { + shape_classifier = new tesseract::TessClassifier(false, classify); + } else if (classifier == CN_CUBE) { + shape_classifier = new tesseract::CubeClassifier(tesseract); + } else if (classifier == CN_CUBETESS) { + shape_classifier = new tesseract::CubeTessClassifier(tesseract); + } else { + fprintf(stderr, "%s tester not yet implemented\n", classifer_name); + return NULL; + } + tprintf("Testing classifier %s:\n", classifer_name); + return shape_classifier; +} + // This program has complex setup requirements, so here is some help: // Two different modes, tr files and serialized mastertrainer. // From tr files: @@ -64,69 +131,28 @@ const char* names[] = {"pruner", "full", "cube", "cubetess", NULL }; // with an input trainer.) int main(int argc, char **argv) { ParseArguments(&argc, &argv); + STRING file_prefix; + tesseract::MasterTrainer* trainer = tesseract::LoadTrainingData( + argc, argv, false, NULL, &file_prefix); + tesseract::TessBaseAPI* api; // Decode the classifier string. - ClassifierName classifier = CN_COUNT; - for (int c = 0; c < CN_COUNT; ++c) { - if (strcmp(FLAGS_classifier.c_str(), names[c]) == 0) { - classifier = static_cast(c); - break; - } - } - if (classifier == CN_COUNT) { - fprintf(stderr, "Invalid classifier name:%s\n", FLAGS_classifier.c_str()); + tesseract::ShapeClassifier* shape_classifier = InitializeClassifier( + FLAGS_classifier.c_str(), trainer->unicharset(), argc, argv, &api); + if (shape_classifier == NULL) { + fprintf(stderr, "Classifier init failed!:%s\n", FLAGS_classifier.c_str()); return 1; } - STRING file_prefix; - tesseract::MasterTrainer* trainer = tesseract::LoadTrainingData( - argc, argv, true, NULL, &file_prefix); // We want to test junk as well if it is available. - trainer->IncludeJunk(); + // trainer->IncludeJunk(); // We want to test with replicated samples too. trainer->ReplicateAndRandomizeSamplesIfRequired(); - // We need to initialize tesseract to test. - tesseract::TessBaseAPI api; - tesseract::OcrEngineMode engine_mode = tesseract::OEM_TESSERACT_ONLY; - if (classifier == CN_CUBE || classifier == CN_CUBETESS) - engine_mode = tesseract::OEM_TESSERACT_CUBE_COMBINED; - if (api.Init(FLAGS_tessdata_dir.c_str(), FLAGS_lang.c_str(), - engine_mode) < 0) { - fprintf(stderr, "Tesseract initialization failed!\n"); - return 1; - } - tesseract::ShapeClassifier* shape_classifier = NULL; - tesseract::Tesseract* tesseract = - const_cast(api.tesseract()); - tesseract::Classify* classify = - reinterpret_cast(tesseract); - // Copy the shape_table from the classifier and add the space character if - // not already present to count junk. - tesseract::ShapeTable shape_table; - shape_table.set_unicharset(classify->shape_table()->unicharset()); - shape_table.AppendMasterShapes(*classify->shape_table()); - if (shape_table.FindShape(0, -1) < 0) - shape_table.AddShape(0, 0); - if (classifier == CN_PRUNER) { - shape_classifier = new tesseract::TessClassifier(true, classify); - } else if (classifier == CN_FULL) { - shape_classifier = new tesseract::TessClassifier(false, classify); - } else if (classifier == CN_CUBE) { - shape_classifier = new tesseract::CubeClassifier(tesseract); - } else if (classifier == CN_CUBETESS) { - shape_classifier = new tesseract::CubeTessClassifier(tesseract); - } else { - fprintf(stderr, "%s tester not yet implemented\n", - FLAGS_classifier.c_str()); - return 1; - } - tprintf("Testing classifier %s:\n", FLAGS_classifier.c_str()); - trainer->TestClassifierOnSamples(3, false, shape_classifier, NULL); - if (classifier != CN_CUBE && classifier != CN_CUBETESS) { - // Test with replicated samples as well. - trainer->TestClassifierOnSamples(3, true, shape_classifier, NULL); - } + trainer->TestClassifierOnSamples(tesseract:: CT_UNICHAR_TOP1_ERR, + MAX(3, FLAGS_debug_level), false, + shape_classifier, NULL); delete shape_classifier; + delete api; delete trainer; return 0; diff --git a/training/combine_tessdata.cpp b/training/combine_tessdata.cpp index ab13d7dcdb..dffd6f40d3 100644 --- a/training/combine_tessdata.cpp +++ b/training/combine_tessdata.cpp @@ -129,13 +129,15 @@ int main(int argc, char **argv) { } else { printf("Usage for combining tessdata components:\n" " %s language_data_path_prefix\n" - " (e.g. %s tessdata/eng.)\n\n", argv[0], argv[0]); + " (e.g. %s tessdata/eng.)\n\n", argv[0], argv[0]); printf("Usage for extracting tessdata components:\n" " %s -e traineddata_file [output_component_file...]\n" - " (e.g. %s -e eng.traineddata eng.unicharset)\n\n", argv[0], argv[0]); + " (e.g. %s -e eng.traineddata eng.unicharset)\n\n", + argv[0], argv[0]); printf("Usage for overwriting tessdata components:\n" " %s -o traineddata_file [input_component_file...]\n" - " (e.g. %s -o eng.traineddata eng.unicharset)\n\n", argv[0], argv[0]); + " (e.g. %s -o eng.traineddata eng.unicharset)\n\n", + argv[0], argv[0]); printf("Usage for unpacking all tessdata components:\n" " %s -u traineddata_file output_path_prefix\n" " (e.g. %s -u eng.traineddata tmp/eng.)\n", argv[0], argv[0]); diff --git a/training/commandlineflags.cpp b/training/commandlineflags.cpp new file mode 100644 index 0000000000..4b82bcdc36 --- /dev/null +++ b/training/commandlineflags.cpp @@ -0,0 +1,319 @@ +#include "commandlineflags.h" + +#ifdef USE_STD_NAMESPACE + +namespace tesseract { +bool IntFlagExists(const char* flag_name, inT32* value) { + STRING full_flag_name("FLAGS_"); + full_flag_name += flag_name; + GenericVector empty; + IntParam *p = ParamUtils::FindParam( + full_flag_name.string(), GlobalParams()->int_params, empty); + if (p == NULL) return false; + *value = (inT32)(*p); + return true; +} + +bool DoubleFlagExists(const char* flag_name, double* value) { + STRING full_flag_name("FLAGS_"); + full_flag_name += flag_name; + GenericVector empty; + DoubleParam *p = ParamUtils::FindParam( + full_flag_name.string(), GlobalParams()->double_params, empty); + if (p == NULL) return false; + *value = static_cast(*p); + return true; +} + +bool BoolFlagExists(const char* flag_name, bool* value) { + STRING full_flag_name("FLAGS_"); + full_flag_name += flag_name; + GenericVector empty; + BoolParam *p = ParamUtils::FindParam( + full_flag_name.string(), GlobalParams()->bool_params, empty); + if (p == NULL) return false; + *value = (BOOL8)(*p); + return true; +} + +bool StringFlagExists(const char* flag_name, const char** value) { + STRING full_flag_name("FLAGS_"); + full_flag_name += flag_name; + GenericVector empty; + StringParam *p = ParamUtils::FindParam( + full_flag_name.string(), GlobalParams()->string_params, empty); + *value = (p != NULL) ? p->string() : NULL; + return p != NULL; +} + + +void SetIntFlagValue(const char* flag_name, const inT32 new_val) { + STRING full_flag_name("FLAGS_"); + full_flag_name += flag_name; + GenericVector empty; + IntParam *p = ParamUtils::FindParam( + full_flag_name.string(), GlobalParams()->int_params, empty); + ASSERT_HOST(p != NULL); + p->set_value(new_val); +} + +void SetDoubleFlagValue(const char* flag_name, const double new_val) { + STRING full_flag_name("FLAGS_"); + full_flag_name += flag_name; + GenericVector empty; + DoubleParam *p = ParamUtils::FindParam( + full_flag_name.string(), GlobalParams()->double_params, empty); + ASSERT_HOST(p != NULL); + p->set_value(new_val); +} + +void SetBoolFlagValue(const char* flag_name, const bool new_val) { + STRING full_flag_name("FLAGS_"); + full_flag_name += flag_name; + GenericVector empty; + BoolParam *p = ParamUtils::FindParam( + full_flag_name.string(), GlobalParams()->bool_params, empty); + ASSERT_HOST(p != NULL); + p->set_value(new_val); +} + +void SetStringFlagValue(const char* flag_name, const char* new_val) { + STRING full_flag_name("FLAGS_"); + full_flag_name += flag_name; + GenericVector empty; + StringParam *p = ParamUtils::FindParam( + full_flag_name.string(), GlobalParams()->string_params, empty); + ASSERT_HOST(p != NULL); + p->set_value(STRING(new_val)); +} + +bool SafeAtoi(const char* str, int* val) { + char *endptr = NULL; + *val = strtol(str, &endptr, 10); + return endptr != NULL && *endptr == '\0'; +} + +bool SafeAtod(const char* str, double* val) { + char *endptr = NULL; + *val = strtod(str, &endptr); + return endptr != NULL && *endptr == '\0'; +} + +void PrintCommandLineFlags() { + const char* kFlagNamePrefix = "FLAGS_"; + const int kFlagNamePrefixLen = strlen(kFlagNamePrefix); + for (int i = 0; i < GlobalParams()->int_params.size(); ++i) { + if (!strncmp(GlobalParams()->int_params[i]->name_str(), + kFlagNamePrefix, kFlagNamePrefixLen)) { + tprintf(" --%s %s (type:int default:%d)\n", + GlobalParams()->int_params[i]->name_str() + kFlagNamePrefixLen, + GlobalParams()->int_params[i]->info_str(), + inT32(*(GlobalParams()->int_params[i]))); + } + } + for (int i = 0; i < GlobalParams()->double_params.size(); ++i) { + if (!strncmp(GlobalParams()->double_params[i]->name_str(), + kFlagNamePrefix, kFlagNamePrefixLen)) { + tprintf(" --%s %s (type:double default:%g)\n", + GlobalParams()->double_params[i]->name_str() + kFlagNamePrefixLen, + GlobalParams()->double_params[i]->info_str(), + static_cast(*(GlobalParams()->double_params[i]))); + } + } + for (int i = 0; i < GlobalParams()->bool_params.size(); ++i) { + if (!strncmp(GlobalParams()->bool_params[i]->name_str(), + kFlagNamePrefix, kFlagNamePrefixLen)) { + tprintf(" --%s %s (type:bool default:%s)\n", + GlobalParams()->bool_params[i]->name_str() + kFlagNamePrefixLen, + GlobalParams()->bool_params[i]->info_str(), + (BOOL8(*(GlobalParams()->bool_params[i])) ? "true" : "false")); + } + } + for (int i = 0; i < GlobalParams()->string_params.size(); ++i) { + if (!strncmp(GlobalParams()->string_params[i]->name_str(), + kFlagNamePrefix, kFlagNamePrefixLen)) { + tprintf(" --%s %s (type:string default:%s)\n", + GlobalParams()->string_params[i]->name_str() + kFlagNamePrefixLen, + GlobalParams()->string_params[i]->info_str(), + GlobalParams()->string_params[i]->string()); + } + } +} + + +void ParseCommandLineFlags(const char* usage, + int* argc, char*** argv, + const bool remove_flags) { + unsigned int i = 1; + for (i = 1; i < *argc; ++i) { + const char* current_arg = (*argv)[i]; + // If argument does not start with a hyphen then break. + if (current_arg[0] != '-') { + break; + } + // Position current_arg after startings hyphens. We treat a sequence of + // consecutive hyphens of any length identically. + while (*current_arg == '-') { + ++current_arg; + } + // If this is asking for usage, print the help message and abort. + if (!strcmp(current_arg, "help") || + !strcmp(current_arg, "helpshort")) { + tprintf("USAGE: %s\n", usage); + PrintCommandLineFlags(); + exit(0); + } + // Find the starting position of the value if it was specified in this + // string. + const char* equals_position = strchr(current_arg, '='); + const char* rhs = NULL; + if (equals_position != NULL) { + rhs = equals_position + 1; + } + // Extract the flag name. + STRING lhs; + if (equals_position == NULL) { + lhs = current_arg; + } else { + lhs.assign(current_arg, equals_position - current_arg); + } + if (!lhs.length()) { + tprintf("ERROR: Bad argument: %s\n", (*argv)[i]); + exit(1); + } + + // Find the flag name in the list of global flags. + // inT32 flag + inT32 int_val; + if (IntFlagExists(lhs.string(), &int_val)) { + if (rhs != NULL) { + if (!strlen(rhs)) { + // Bad input of the format --int_flag= + tprintf("ERROR: Bad argument: %s\n", (*argv)[i]); + exit(1); + } + if (!SafeAtoi(rhs, &int_val)) { + tprintf("ERROR: Could not parse int from %s in flag %s\n", + rhs, (*argv)[i]); + exit(1); + } + } else { + // We need to parse the next argument + if (i + 1 >= *argc) { + tprintf("ERROR: Could not find value argument for flag %s\n", + lhs.string()); + exit(1); + } else { + ++i; + if (!SafeAtoi((*argv)[i], &int_val)) { + tprintf("ERROR: Could not parse inT32 from %s\n", (*argv)[i]); + exit(1); + } + } + } + SetIntFlagValue(lhs.string(), int_val); + continue; + } + + // double flag + double double_val; + if (DoubleFlagExists(lhs.string(), &double_val)) { + if (rhs != NULL) { + if (!strlen(rhs)) { + // Bad input of the format --double_flag= + tprintf("ERROR: Bad argument: %s\n", (*argv)[i]); + exit(1); + } + if (!SafeAtod(rhs, &double_val)) { + tprintf("ERROR: Could not parse double from %s in flag %s\n", + rhs, (*argv)[i]); + exit(1); + } + } else { + // We need to parse the next argument + if (i + 1 >= *argc) { + tprintf("ERROR: Could not find value argument for flag %s\n", + lhs.string()); + exit(1); + } else { + ++i; + if (!SafeAtod((*argv)[i], &double_val)) { + tprintf("ERROR: Could not parse double from %s\n", (*argv)[i]); + exit(1); + } + } + } + SetDoubleFlagValue(lhs.string(), double_val); + continue; + } + + // Bool flag. Allow input forms --flag (equivalent to --flag=true), + // --flag=false, --flag=true, --flag=0 and --flag=1 + bool bool_val; + if (BoolFlagExists(lhs.string(), &bool_val)) { + if (rhs == NULL) { + // --flag form + bool_val = true; + } else { + if (!strlen(rhs)) { + // Bad input of the format --bool_flag= + tprintf("ERROR: Bad argument: %s\n", (*argv)[i]); + exit(1); + } + if (!strcmp(rhs, "false") || !strcmp(rhs, "0")) { + bool_val = false; + } else if (!strcmp(rhs, "true") || !strcmp(rhs, "1")) { + bool_val = true; + } else { + tprintf("ERROR: Could not parse bool from flag %s\n", (*argv)[i]); + exit(1); + } + } + SetBoolFlagValue(lhs.string(), bool_val); + continue; + } + + // string flag + const char* string_val; + if (StringFlagExists(lhs.string(), &string_val)) { + if (rhs != NULL) { + string_val = rhs; + } else { + // Pick the next argument + if (i + 1 >= *argc) { + tprintf("ERROR: Could not find string value for flag %s\n", + lhs.string()); + exit(1); + } else { + string_val = (*argv)[++i]; + } + } + SetStringFlagValue(lhs.string(), string_val); + continue; + } + + // Flag was not found. Exit with an error message. + tprintf("ERROR: Non-existent flag %s\n", (*argv)[i]); + exit(1); + } // for each argv + if (remove_flags) { + (*argv)[i - 1] = (*argv)[0]; + (*argv) += (i - 1); + (*argc) -= (i - 1); + } +} +} // namespace tesseract + +#else + +#include "base/init_google.h" + +namespace tesseract { +void ParseCommandLineFlags(const char* usage, + int* argc, char*** argv, + const bool remove_flags) { + InitGoogle(usage, argc, argv, remove_flags); +} +} // namespace tesseract + +#endif diff --git a/training/commandlineflags.h b/training/commandlineflags.h new file mode 100644 index 0000000000..11b0f82c87 --- /dev/null +++ b/training/commandlineflags.h @@ -0,0 +1,83 @@ +/********************************************************************** + * File: commandlineflags.h + * Description: Header file for commandline flag parsing. + * Author: Ranjith Unnikrishnan + * Created: July 2013 + * + * (C) Copyright 2013, Google Inc. + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** http://www.apache.org/licenses/LICENSE-2.0 + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + * + **********************************************************************/ +#ifndef TESSERACT_TRAINING_COMMANDLINEFLAGS_H_ +#define TESSERACT_TRAINING_COMMANDLINEFLAGS_H_ + +#ifdef USE_STD_NAMESPACE + +#include +#include "tprintf.h" +#include "params.h" + +#define INT_PARAM_FLAG(name, val, comment) \ + INT_VAR(FLAGS_##name, val, comment) +#define DECLARE_INT_PARAM_FLAG(name) \ + extern INT_VAR_H(FLAGS_##name, 0, "") +#define DOUBLE_PARAM_FLAG(name, val, comment) \ + double_VAR(FLAGS_##name, val, comment) +#define DECLARE_DOUBLE_PARAM_FLAG(name) \ + extern double_VAR_H(FLAGS_##name, "", "") +#define BOOL_PARAM_FLAG(name, val, comment) \ + BOOL_VAR(FLAGS_##name, val, comment) +#define DECLARE_BOOL_PARAM_FLAG(name) \ + extern BOOL_VAR_H(FLAGS_##name, 0, "") +#define STRING_PARAM_FLAG(name, val, comment) \ + STRING_VAR(FLAGS_##name, val, comment) +#define DECLARE_STRING_PARAM_FLAG(name) \ + extern STRING_VAR_H(FLAGS_##name, "", "") + +#else + +#include "base/commandlineflags.h" +#define INT_PARAM_FLAG(name, val, comment) \ + DEFINE_int32(name, val, comment) +#define DECLARE_INT_PARAM_FLAG(name) \ + DECLARE_int32(name) +#define DOUBLE_PARAM_FLAG(name, val, comment) \ + DEFINE_double(name, val, comment) +#define DECLARE_DOUBLE_PARAM_FLAG(name) \ + DECLARE_double(name) +#define BOOL_PARAM_FLAG(name, val, comment) \ + DEFINE_bool(name, val, comment) +#define DECLARE_BOOL_PARAM_FLAG(name) \ + DECLARE_bool(name) +#define STRING_PARAM_FLAG(name, val, comment) \ + DEFINE_string(name, val, comment) +#define DECLARE_STRING_PARAM_FLAG(name) \ + DECLARE_string(name) + +#endif + +namespace tesseract { + +// Parse commandline flags and values. Prints the usage string and exits on +// input of --help or --helpshort. +// +// If remove_flags is true, the argv pointer is advanced so that (*argv)[1] +// points to the first non-flag argument, (*argv)[0] points to the same string +// as before, and argc is decremented to reflect the new shorter length of argv. +// eg. If the input *argv is +// { "program", "--foo=4", "--bar=true", "file1", "file2" } with *argc = 5, the +// output *argv is { "program", "file1", "file2" } with *argc = 3 +void ParseCommandLineFlags(const char* usage, int* argc, + char*** argv, const bool remove_flags); + +} + +#endif // TESSERACT_TRAINING_COMMANDLINEFLAGS_H_ diff --git a/training/commontraining.cpp b/training/commontraining.cpp index c76443ada7..cbb5233e5f 100644 --- a/training/commontraining.cpp +++ b/training/commontraining.cpp @@ -13,30 +13,27 @@ #include "commontraining.h" -#ifndef USE_STD_NAMESPACE -#include "base/init_google.h" -#include "base/commandlineflags.h" -#endif #include "allheaders.h" #include "ccutil.h" #include "classify.h" -#include "oldlist.h" -#include "globals.h" -#include "mf.h" -#include "clusttool.h" #include "cluster.h" -#include "tessopt.h" +#include "clusttool.h" #include "efio.h" #include "emalloc.h" #include "featdefs.h" #include "fontinfo.h" +#include "freelist.h" +#include "globals.h" #include "intfeaturespace.h" #include "mastertrainer.h" -#include "tessdatamanager.h" -#include "tprintf.h" -#include "freelist.h" +#include "mf.h" +#include "ndminx.h" +#include "oldlist.h" #include "params.h" #include "shapetable.h" +#include "tessdatamanager.h" +#include "tessopt.h" +#include "tprintf.h" #include "unicity_table.h" #include @@ -48,9 +45,12 @@ using tesseract::ParamUtils; using tesseract::ShapeTable; // Global Variables. + // global variable to hold configuration parameters to control clustering // -M 0.625 -B 0.05 -I 1.0 -C 1e-6. CLUSTERCONFIG Config = { elliptical, 0.625, 0.05, 1.0, 1e-6, 0 }; +FEATURE_DEFS_STRUCT feature_defs; +CCUtil ccutil; INT_PARAM_FLAG(debug_level, 0, "Level of Trainer debugging"); INT_PARAM_FLAG(load_images, 0, "Load images with tr files"); @@ -60,114 +60,51 @@ STRING_PARAM_FLAG(F, "font_properties", "File listing font properties"); STRING_PARAM_FLAG(X, "", "File listing font xheights"); STRING_PARAM_FLAG(U, "unicharset", "File to load unicharset from"); STRING_PARAM_FLAG(O, "", "File to write unicharset to"); -STRING_PARAM_FLAG(input_trainer, "", "File to load trainer from"); +STRING_PARAM_FLAG(T, "", "File to load trainer from"); STRING_PARAM_FLAG(output_trainer, "", "File to write trainer to"); STRING_PARAM_FLAG(test_ch, "", "UTF8 test character string"); +DOUBLE_PARAM_FLAG(clusterconfig_min_samples_fraction, Config.MinSamples, + "Min number of samples per proto as % of total"); +DOUBLE_PARAM_FLAG(clusterconfig_max_illegal, Config.MaxIllegal, + "Max percentage of samples in a cluster which have more" + " than 1 feature in that cluster"); +DOUBLE_PARAM_FLAG(clusterconfig_independence, Config.Independence, + "Desired independence between dimensions"); +DOUBLE_PARAM_FLAG(clusterconfig_confidence, Config.Confidence, + "Desired confidence in prototypes created"); -// The usage strings are different as the DEFINE_* flags are available on -// the command line, but the *_VAR flags are set through a config file with -// some of them available through special command-line args. -#ifndef USE_STD_NAMESPACE -const char* kUsage = "[flags] [ .tr files ... ]\n"; -#else -const char* kUsage = "[-c configfile]\n" - "\t[-D Directory]\n" - "\t[-M MinSamples] [-B MaxBad] [-I Independence] [-C Confidence]\n" - "\t[-U InputUnicharset]\n" - "\t[-O OutputUnicharset]\n" - "\t[-F FontInfoFile]\n" - "\t[-X InputXHeightsFile]\n" - "\t[-S InputShapeTable]\n" - "\t[ .tr files ... ]\n"; -#endif - -FEATURE_DEFS_STRUCT feature_defs; -CCUtil ccutil; - -/*---------------------------------------------------------------------------*/ -void ParseArguments(int* argc, char ***argv) { /* ** Parameters: - ** argc number of command line arguments to parse - ** argv command line arguments + ** argc number of command line arguments to parse + ** argv command line arguments ** Globals: - ** ShowSignificantProtos flag controlling proto display - ** ShowInsignificantProtos flag controlling proto display - ** Config current clustering parameters - ** tessoptarg, tessoptind defined by tessopt sys call - ** Argc, Argv global copies of argc and argv + ** Config current clustering parameters ** Operation: ** This routine parses the command line arguments that were - ** passed to the program. The legal arguments are shown in the usage - ** message below: - + ** passed to the program and ses them to set relevant + ** training-related global parameters ** Return: none ** Exceptions: Illegal options terminate the program. - ** History: 7/24/89, DSJ, Created. */ -#ifndef USE_STD_NAMESPACE - InitGoogle(kUsage, argc, argv, true); - tessoptind = 1; -#else - int Option; - int ParametersRead; - BOOL8 Error; - - Error = FALSE; - while ((Option = tessopt(*argc, *argv, "F:O:U:D:C:I:M:B:S:X:c:")) != EOF) { - switch (Option) { - case 'C': - ParametersRead = sscanf(tessoptarg, "%lf", &(Config.Confidence) ); - if ( ParametersRead != 1 ) Error = TRUE; - else if ( Config.Confidence > 1 ) Config.Confidence = 1; - else if ( Config.Confidence < 0 ) Config.Confidence = 0; - break; - case 'I': - ParametersRead = sscanf(tessoptarg, "%f", &(Config.Independence) ); - if ( ParametersRead != 1 ) Error = TRUE; - else if ( Config.Independence > 1 ) Config.Independence = 1; - else if ( Config.Independence < 0 ) Config.Independence = 0; - break; - case 'M': - ParametersRead = sscanf(tessoptarg, "%f", &(Config.MinSamples) ); - if ( ParametersRead != 1 ) Error = TRUE; - else if ( Config.MinSamples > 1 ) Config.MinSamples = 1; - else if ( Config.MinSamples < 0 ) Config.MinSamples = 0; - break; - case 'B': - ParametersRead = sscanf(tessoptarg, "%f", &(Config.MaxIllegal) ); - if ( ParametersRead != 1 ) Error = TRUE; - else if ( Config.MaxIllegal > 1 ) Config.MaxIllegal = 1; - else if ( Config.MaxIllegal < 0 ) Config.MaxIllegal = 0; - break; - case 'c': - FLAGS_configfile.set_value(tessoptarg); - break; - case 'D': - FLAGS_D.set_value(tessoptarg); - break; - case 'U': - FLAGS_U.set_value(tessoptarg); - break; - case 'O': - FLAGS_O.set_value(tessoptarg); - break; - case 'F': - FLAGS_F.set_value(tessoptarg); - break; - case 'X': - FLAGS_X.set_value(tessoptarg); - break; - case '?': - Error = TRUE; - break; - } - if (Error) { - fprintf(stderr, "Usage: %s %s\n", (*argv)[0], kUsage); - exit(2); - } +void ParseArguments(int* argc, char ***argv) { + STRING usage; + if (*argc) { + usage += (*argv)[0]; } -#endif + usage += " [.tr files ...]"; + tesseract::ParseCommandLineFlags(usage.c_str(), argc, argv, true); + // Record the index of the first non-flag argument to 1, since we set + // remove_flags to true when parsing the flags. + tessoptind = 1; + // Set some global values based on the flags. + Config.MinSamples = + MAX(0.0, MIN(1.0, double(FLAGS_clusterconfig_min_samples_fraction))); + Config.MaxIllegal = + MAX(0.0, MIN(1.0, double(FLAGS_clusterconfig_max_illegal))); + Config.Independence = + MAX(0.0, MIN(1.0, double(FLAGS_clusterconfig_independence))); + Config.Confidence = + MAX(0.0, MIN(1.0, double(FLAGS_clusterconfig_confidence))); // Set additional parameters from config file if specified. if (!FLAGS_configfile.empty()) { tesseract::ParamUtils::ReadParamsFile( @@ -175,10 +112,9 @@ void ParseArguments(int* argc, char ***argv) { tesseract::SET_PARAM_CONSTRAINT_NON_INIT_ONLY, ccutil.params()); } -} // ParseArguments +} namespace tesseract { - // Helper loads shape table from the given file. ShapeTable* LoadShapeTable(const STRING& file_prefix) { ShapeTable* shape_table = NULL; @@ -226,7 +162,7 @@ void WriteShapeTable(const STRING& file_prefix, const ShapeTable& shape_table) { // Initializes feature_defs and IntegerFX. // Loads the shape_table if shape_table != NULL. // Loads initial unicharset from -U command-line option. -// If FLAGS_input_trainer is set, loads the majority of data from there, else: +// If FLAGS_T is set, loads the majority of data from there, else: // Loads font info from -F option. // Loads xheights from -X option. // Loads samples from .tr files in remaining command-line args. @@ -262,7 +198,9 @@ MasterTrainer* LoadTrainingData(int argc, const char* const * argv, shape_analysis, replication, FLAGS_debug_level); - if (FLAGS_input_trainer.empty()) { + IntFeatureSpace fs; + fs.Init(kBoostXYBuckets, kBoostXYBuckets, kBoostDirBuckets); + if (FLAGS_T.empty()) { trainer->LoadUnicharset(FLAGS_U.c_str()); // Get basic font information from font_properties. if (!FLAGS_F.empty()) { @@ -277,16 +215,12 @@ MasterTrainer* LoadTrainingData(int argc, const char* const * argv, return NULL; } } - IntFeatureSpace fs; - fs.Init(kBoostXYBuckets, kBoostXYBuckets, kBoostDirBuckets); trainer->SetFeatureSpace(fs); const char* page_name; // Load training data from .tr files on the command line. while ((page_name = GetNextFilename(argc, argv)) != NULL) { tprintf("Reading %s ...\n", page_name); - FILE* fp = Efopen(page_name, "rb"); - trainer->ReadTrainingSamples(fp, feature_defs, false); - fclose(fp); + trainer->ReadTrainingSamples(page_name, feature_defs, false); // If there is a file with [lang].[fontname].exp[num].fontinfo present, // read font spacing information in to fontinfo_table. @@ -320,11 +254,11 @@ MasterTrainer* LoadTrainingData(int argc, const char* const * argv, } else { bool success = false; tprintf("Loading master trainer from file:%s\n", - FLAGS_input_trainer.c_str()); - FILE* fp = fopen(FLAGS_input_trainer.c_str(), "rb"); + FLAGS_T.c_str()); + FILE* fp = fopen(FLAGS_T.c_str(), "rb"); if (fp == NULL) { tprintf("Can't read file %s to initialize master trainer\n", - FLAGS_input_trainer.c_str()); + FLAGS_T.c_str()); } else { success = trainer->DeSerialize(false, fp); fclose(fp); @@ -334,6 +268,7 @@ MasterTrainer* LoadTrainingData(int argc, const char* const * argv, delete trainer; return NULL; } + trainer->SetFeatureSpace(fs); } trainer->PreTrainingSetup(); if (!FLAGS_O.empty() && diff --git a/training/commontraining.h b/training/commontraining.h index 8fc0d6de8e..7f38cf51c0 100644 --- a/training/commontraining.h +++ b/training/commontraining.h @@ -14,31 +14,11 @@ #ifndef TESSERACT_TRAINING_COMMONTRAINING_H__ #define TESSERACT_TRAINING_COMMONTRAINING_H__ -#include "oldlist.h" #include "cluster.h" -#include "intproto.h" +#include "commandlineflags.h" #include "featdefs.h" - -// Macros to merge tesseract params with command-line flags. -#ifdef USE_STD_NAMESPACE -#include "params.h" -# define INT_PARAM_FLAG(name, val, comment) \ - INT_VAR(FLAGS_##name, val, comment) -# define DECLARE_INT_PARAM_FLAG(name) extern INT_VAR_H(FLAGS_##name, 0, "") -# define STRING_PARAM_FLAG(name, val, comment) \ - STRING_VAR(FLAGS_##name, val, comment) -# define DECLARE_STRING_PARAM_FLAG(name) \ - extern STRING_VAR_H(FLAGS_##name, "", "") -# define c_str string -#else -#include "base/commandlineflags.h" -# define INT_PARAM_FLAG(name, val, comment) \ - DEFINE_int32(name, val, comment) -# define DECLARE_INT_PARAM_FLAG(name) DECLARE_int32(name) -# define STRING_PARAM_FLAG(name, val, comment) \ - DEFINE_string(name, val, comment) -# define DECLARE_STRING_PARAM_FLAG(name) DECLARE_string(name) -#endif +#include "intproto.h" +#include "oldlist.h" namespace tesseract { class Classify; @@ -82,6 +62,7 @@ typedef MERGE_CLASS_NODE* MERGE_CLASS; void ParseArguments(int* argc, char*** argv); namespace tesseract { + // Helper loads shape table from the given file. ShapeTable* LoadShapeTable(const STRING& file_prefix); // Helper to write the shape_table. diff --git a/training/fileio.cpp b/training/fileio.cpp new file mode 100644 index 0000000000..e2be37d047 --- /dev/null +++ b/training/fileio.cpp @@ -0,0 +1,137 @@ +#include "fileio.h" + +#include +#include +#include + +#include "tprintf.h" + +namespace tesseract { + +/////////////////////////////////////////////////////////////////////////////// +// File:: +/////////////////////////////////////////////////////////////////////////////// +FILE* File::Open(const string& filename, const string& mode) { + return fopen(filename.c_str(), mode.c_str()); +} + +FILE* File::OpenOrDie(const string& filename, + const string& mode) { + FILE* stream = fopen(filename.c_str(), mode.c_str()); + if (stream == NULL) { + tprintf("Unable to open '%s' in mode '%s'", filename.c_str(), mode.c_str()); + } + return stream; +} + +void File::WriteStringToFileOrDie(const string& str, + const string& filename) { + FILE* stream = fopen(filename.c_str(), "wb"); + if (stream == NULL) { + tprintf("Unable to open '%s' for writing", filename.c_str()); + return; + } + fputs(str.c_str(), stream); + ASSERT_HOST(fclose(stream) == 0); +} + +bool File::Readable(const string& filename) { + FILE* stream = fopen(filename.c_str(), "rb"); + if (stream == NULL) { + return false; + } + fclose(stream); + return true; +} + +bool File::ReadFileToString(const string& filename, string* out) { + FILE* stream = File::Open(filename.c_str(), "rb"); + if (stream == NULL) + return false; + InputBuffer in(stream); + *out = ""; + string temp; + while (in.ReadLine(&temp)) { + *out += temp; + *out += '\n'; + } + return in.CloseFile(); +} + +void File::ReadFileToStringOrDie(const string& filename, string* out) { + ASSERT_HOST(ReadFileToString(filename, out)); +} + +/////////////////////////////////////////////////////////////////////////////// +// InputBuffer:: +/////////////////////////////////////////////////////////////////////////////// +InputBuffer::InputBuffer(FILE* stream) + : stream_(stream) { + fseek(stream_, 0, SEEK_END); + filesize_ = ftell(stream_); + fseek(stream_, 0, SEEK_SET); +} + +InputBuffer::InputBuffer(FILE* stream, size_t) + : stream_(stream) { + fseek(stream_, 0, SEEK_END); + filesize_ = ftell(stream_); + fseek(stream_, 0, SEEK_SET); +} + +InputBuffer::~InputBuffer() { + if (stream_ != NULL) { + fclose(stream_); + } +} + +bool InputBuffer::ReadLine(string* out) { + ASSERT_HOST(stream_ != NULL); + char* line = NULL; + size_t line_size; + int len = getline(&line, &line_size, stream_); + + if (len < 0) + return false; + if (len >= 1 && line[len - 1] == '\n') + line[len - 1] = '\0'; + *out = string(line); + free(line); + return true; +} + +bool InputBuffer::CloseFile() { + int ret = fclose(stream_); + stream_ = NULL; + return ret == 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// OutputBuffer:: +/////////////////////////////////////////////////////////////////////////////// + +OutputBuffer::OutputBuffer(FILE* stream) + : stream_(stream) { +} + +OutputBuffer::OutputBuffer(FILE* stream, size_t) + : stream_(stream) { +} + +OutputBuffer::~OutputBuffer() { + if (stream_ != NULL) { + fclose(stream_); + } +} + +void OutputBuffer::WriteString(const string& str) { + fputs(str.c_str(), stream_); +} + +bool OutputBuffer::CloseFile() { + int ret = fclose(stream_); + stream_ = NULL; + return ret == 0; +} + +} // namespace tesseract diff --git a/training/fileio.h b/training/fileio.h new file mode 100644 index 0000000000..3ef3cfb3c3 --- /dev/null +++ b/training/fileio.h @@ -0,0 +1,93 @@ +/********************************************************************** + * File: fileio.h + * Description: File I/O utilities. + * Author: Samuel Charron + * Created: Tuesday, July 9, 2013 + * + * (C) Copyright 2013, Google Inc. + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required + * by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS + * OF ANY KIND, either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + * + **********************************************************************/ +#ifndef TESSERACT_TRAINING_FILEIO_H_ +#define TESSERACT_TRAINING_FILEIO_H_ + +#include +#include +#include + +#ifdef USE_STD_NAMESPACE +using std::string; +#endif + +namespace tesseract { + +// A class to manipulate FILE*s. +class File { + public: + // Try to open the file 'filename' in mode 'mode'. + // Stop the program if it cannot open it. + static FILE* OpenOrDie(const string& filename, const string& mode); + static FILE* Open(const string& filename, const string& mode); + + // Try to open the file 'filename' and to write 'str' in it. + // Stop the program if it fails. + static void WriteStringToFileOrDie(const string& str, const string& filename); + + // Return true if the file 'filename' is readable. + static bool Readable(const string& filename); + + static void ReadFileToStringOrDie(const string& filename, string* out); + static bool ReadFileToString(const string& filename, string* out); +}; + +// A class to manipulate Files for reading. +class InputBuffer { + public: + explicit InputBuffer(FILE* stream); + // 'size' is ignored. + InputBuffer(FILE* stream, size_t size); + + ~InputBuffer(); + + // Read data until end-of-file or a \n is read. + // The data is stored in '*out', excluding the \n if present. + // Return false if an error occurs or at end-of-file, true otherwise. + bool ReadLine(string* out); + + // Close the FILE* used by InputBuffer. + // Return false if an error occurs, true otherwise. + bool CloseFile(); + + private: + FILE* stream_; + int filesize_; +}; + +// A class to manipulate Files for writing. +class OutputBuffer { + public: + explicit OutputBuffer(FILE* stream); + // 'size' is ignored. + OutputBuffer(FILE* stream, size_t size); + + ~OutputBuffer(); + + // Write string 'str' to the open FILE*. + void WriteString(const string& str); + + // Close the FILE* used by InputBuffer. + // Return false if an error occurs, true otherwise. + bool CloseFile(); + + private: + FILE* stream_; +}; + +} // namespace tesseract +#endif // TESSERACT_TRAINING_FILEIO_H_ diff --git a/training/icuerrorcode.h b/training/icuerrorcode.h new file mode 100644 index 0000000000..a606415f04 --- /dev/null +++ b/training/icuerrorcode.h @@ -0,0 +1,66 @@ +/********************************************************************** + * File: icuerrorcode.h + * Description: Wrapper class for UErrorCode, with conversion operators for + * direct use in ICU C and C++ APIs. + * Author: Fredrik Roubert + * Created: Thu July 4 2013 + * + * Features: + * - The constructor initializes the internal UErrorCode to U_ZERO_ERROR, + * removing one common source of errors. + * - Same use in C APIs taking a UErrorCode* (pointer) and C++ taking + * UErrorCode& (reference), via conversion operators. + * - Automatic checking for success when it goes out of scope. On failure, + * the destructor will log an error message and exit. + * + * Most of ICU will handle errors gracefully and provide sensible fallbacks. + * Using IcuErrorCode, it is therefore possible to write very compact code + * that does sensible things on failure and provides logging for debugging. + * + * Example: + * IcuErrorCode icuerrorcode; + * return collator.compareUTF8(a, b, icuerrorcode) == UCOL_EQUAL; + * + * (C) Copyright 2013, Google Inc. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **********************************************************************/ +#ifndef TESSERACT_CCUTIL_ICUERRORCODE_H_ +#define TESSERACT_CCUTIL_ICUERRORCODE_H_ + +#include "tprintf.h" +#include "unicode/errorcode.h" // From libicu + +namespace tesseract { + +class IcuErrorCode : public icu::ErrorCode { + public: + IcuErrorCode() {} + virtual ~IcuErrorCode() { + if (isFailure()) { + handleFailure(); + } + } + + protected: + virtual void handleFailure() const { + tprintf("ICU ERROR: %s", errorName()); + exit(errorCode); + } + + private: + // Disallow implicit copying of object. + IcuErrorCode(const IcuErrorCode&); + void operator=(const IcuErrorCode&); +}; + +} // namespace tesseract +#endif // TESSERACT_CCUTIL_ICUERRORCODE_H_ diff --git a/training/normstrngs.cpp b/training/normstrngs.cpp new file mode 100644 index 0000000000..7d12dfde99 --- /dev/null +++ b/training/normstrngs.cpp @@ -0,0 +1,150 @@ +#include "normstrngs.h" + +#include "icuerrorcode.h" +#include "unichar.h" +#include "unicode/normalizer2.h" // From libicu +#include "unicode/unorm2.h" // From libicu + +namespace tesseract { + +void UTF8ToUTF32(const char* utf8_str, GenericVector* str32) { + str32->clear(); + str32->reserve(strlen(utf8_str)); + int len = strlen(utf8_str); + int step = 0; + for (int ch = 0; ch < len; ch += step) { + step = UNICHAR::utf8_step(utf8_str + ch); + if (step > 0) { + UNICHAR uni_ch(utf8_str + ch, step); + (*str32) += uni_ch.first_uni(); + } + } +} + +void UTF32ToUTF8(const GenericVector& str32, STRING* utf8_str) { + utf8_str->ensure(str32.length()); + utf8_str->assign("", 0); + for (int i = 0; i < str32.length(); ++i) { + UNICHAR uni_ch(str32[i]); + char *utf8 = uni_ch.utf8_str(); + if (utf8 != NULL) { + (*utf8_str) += utf8; + delete[] utf8; + } + } +} + +bool is_hyphen_punc(const char32 ch) { + static const int kNumHyphenPuncUnicodes = 13; + static const char32 kHyphenPuncUnicodes[kNumHyphenPuncUnicodes] = { + '-', + 0x2010, 0x2011, 0x2012, 0x2013, 0x2014, 0x2015, // hyphen..horizontal bar + 0x207b, // superscript minus + 0x208b, // subscript minus + 0x2212, // minus sign + 0xfe58, // small em dash + 0xfe63, // small hyphen-minus + 0xff0d, // fullwidth hyphen-minus + }; + for (int i = 0; i < kNumHyphenPuncUnicodes; ++i) { + if (kHyphenPuncUnicodes[i] == ch) + return true; + } + return false; +} + +bool is_single_quote(const char32 ch) { + static const int kNumSingleQuoteUnicodes = 8; + static const char32 kSingleQuoteUnicodes[kNumSingleQuoteUnicodes] = { + '\'', + '`', + 0x2018, // left single quotation mark (English, others) + 0x2019, // right single quotation mark (Danish, Finnish, Swedish, Norw.) + // We may have to introduce a comma set with 0x201a + 0x201B, // single high-reveresed-9 quotation mark (PropList.txt) + 0x2032, // prime + 0x300C, // left corner bracket (East Asian languages) + 0xFF07, // fullwidth apostrophe + }; + for (int i = 0; i < kNumSingleQuoteUnicodes; ++i) { + if (kSingleQuoteUnicodes[i] == ch) + return true; + } + return false; +} + +bool is_double_quote(const char32 ch) { + static const int kNumDoubleQuoteUnicodes = 8; + static const char32 kDoubleQuoteUnicodes[kNumDoubleQuoteUnicodes] = { + '"', + 0x201C, // left double quotation mark (English, others) + 0x201D, // right double quotation mark (Danish, Finnish, Swedish, Norw.) + 0x201F, // double high-reversed-9 quotation mark (PropList.txt) + 0x2033, // double prime + 0x301D, // reversed double prime quotation mark (East Asian langs, horiz.) + 0x301E, // close double prime (East Asian languages written horizontally) + 0xFF02, // fullwidth quotation mark + }; + for (int i = 0; i < kNumDoubleQuoteUnicodes; ++i) { + if (kDoubleQuoteUnicodes[i] == ch) + return true; + } + return false; +} + +STRING NormalizeUTF8String(const char* str8) { + GenericVector str32, out_str32, norm_str; + UTF8ToUTF32(str8, &str32); + for (int i = 0; i < str32.length(); ++i) { + norm_str.clear(); + NormalizeChar32(str32[i], &norm_str); + for (int j = 0; j < norm_str.length(); ++j) { + out_str32.push_back(norm_str[j]); + } + } + STRING out_str8; + UTF32ToUTF8(out_str32, &out_str8); + return out_str8; +} + +void NormalizeChar32(char32 ch, GenericVector* str) { + IcuErrorCode error_code; + const icu::Normalizer2* nfkc = icu::Normalizer2::getInstance( + NULL, "nfkc", UNORM2_COMPOSE, error_code); + error_code.assertSuccess(); + error_code.reset(); + + icu::UnicodeString uch_str(static_cast(ch)); + icu::UnicodeString norm_str = nfkc->normalize(uch_str, error_code); + error_code.assertSuccess(); + + str->clear(); + for (int i = 0; i < norm_str.length(); ++i) { + // If any spaces were added by NFKC, pretend normalization is a nop. + if (norm_str[i] == ' ') { + str->clear(); + str->push_back(ch); + break; + } else { + str->push_back(OCRNormalize(static_cast(norm_str[i]))); + } + } +} + +// Apply just the OCR-specific normalizations and return the normalized char. +char32 OCRNormalize(char32 ch) { + if (is_hyphen_punc(ch)) + return '-'; + else if (is_single_quote(ch)) + return '\''; + else if (is_double_quote(ch)) + return '"'; + return ch; +} + +bool IsOCREquivalent(char32 ch1, char32 ch2) { + return OCRNormalize(ch1) == OCRNormalize(ch2); +} + + +} // namespace tesseract diff --git a/training/normstrngs.h b/training/normstrngs.h new file mode 100644 index 0000000000..6f7612834f --- /dev/null +++ b/training/normstrngs.h @@ -0,0 +1,56 @@ +/********************************************************************** + * File: normstrngs.h + * Description: Utilities to normalize and manipulate UTF-32 and + * UTF-8 strings. + * Author: Ranjith Unnikrishnan + * Created: Thu July 4 2013 + * + * (C) Copyright 2013, Google Inc. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **********************************************************************/ + +#ifndef TESSERACT_CCUTIL_NORMSTRNGS_H_ +#define TESSERACT_CCUTIL_NORMSTRNGS_H_ + +#include "genericvector.h" +#include "strngs.h" + +typedef signed int char32; + +namespace tesseract { + +// UTF-8 to UTF-32 conversion function. +void UTF8ToUTF32(const char* utf8_str, GenericVector* str32); + +// UTF-32 to UTF-8 convesion function. +void UTF32ToUTF8(const GenericVector& str32, STRING* utf8_str); + +// Normalize a single char32 using NFKC + OCR-specific transformations. +// NOTE that proper NFKC may require multiple characters as input. The +// assumption of this function is that the input is already as fully composed +// as it can be, but may require some compatibility normalizations or just +// OCR evaluation related normalizations. +void NormalizeChar32(char32 ch, GenericVector* str); + +// Normalize a UTF8 string. Same as above, but for UTF8-encoded strings, that +// can contain multiple UTF32 code points. +STRING NormalizeUTF8String(const char* str8); + +// Apply just the OCR-specific normalizations and return the normalized char. +char32 OCRNormalize(char32 ch); + +// Returns true if the OCRNormalized ch1 and ch2 are the same. +bool IsOCREquivalent(char32 ch1, char32 ch2); + +} // namespace tesseract + +#endif // TESSERACT_CCUTIL_NORMSTRNGS_H_ diff --git a/training/set_unicharset_properties.cpp b/training/set_unicharset_properties.cpp new file mode 100644 index 0000000000..1c83120612 --- /dev/null +++ b/training/set_unicharset_properties.cpp @@ -0,0 +1,196 @@ +// This program reads a unicharset file, puts the result in a UNICHARSET +// object, fills it with properties about the unichars it contains and writes +// the result back to a file. + +#include +#include +#include + +#include "commandlineflags.h" +#include "fileio.h" +#include "genericvector.h" +#include "icuerrorcode.h" +#include "normstrngs.h" +#include "strngs.h" +#include "unicharset.h" +#include "unicode/uchar.h" // from libicu +#include "unicode/uscript.h" // from libicu + +// The directory that is searched for universal script unicharsets. +STRING_PARAM_FLAG(script_dir, "", + "Directory name for input script unicharsets/xheights"); + +// Flags from commontraining.cpp +DECLARE_STRING_PARAM_FLAG(U); +DECLARE_STRING_PARAM_FLAG(O); +DECLARE_STRING_PARAM_FLAG(X); + +namespace tesseract { + +// Helper sets the character attribute properties and sets up the script table. +// Does not set tops and bottoms. +static void SetupBasicProperties(UNICHARSET* unicharset) { + for (int unichar_id = 0; unichar_id < unicharset->size(); ++unichar_id) { + // Convert any custom ligatures. + const char* unichar_str = unicharset->id_to_unichar(unichar_id); + for (int i = 0; UNICHARSET::kCustomLigatures[i][0] != NULL; ++i) { + if (!strcmp(UNICHARSET::kCustomLigatures[i][1], unichar_str)) { + unichar_str = UNICHARSET::kCustomLigatures[i][0]; + break; + } + } + + // Convert the unichar to UTF32 representation + GenericVector uni_vector; + tesseract::UTF8ToUTF32(unichar_str, &uni_vector); + + // Assume that if the property is true for any character in the string, + // then it holds for the whole "character". + bool unichar_isalpha = false; + bool unichar_islower = false; + bool unichar_isupper = false; + bool unichar_isdigit = false; + bool unichar_ispunct = false; + + for (int i = 0; i < uni_vector.size(); ++i) { + if (u_isalpha(uni_vector[i])) + unichar_isalpha = true; + if (u_islower(uni_vector[i])) + unichar_islower = true; + if (u_isupper(uni_vector[i])) + unichar_isupper = true; + if (u_isdigit(uni_vector[i])) + unichar_isdigit = true; + if (u_ispunct(uni_vector[i])) + unichar_ispunct = true; + } + + unicharset->set_isalpha(unichar_id, unichar_isalpha); + unicharset->set_islower(unichar_id, unichar_islower); + unicharset->set_isupper(unichar_id, unichar_isupper); + unicharset->set_isdigit(unichar_id, unichar_isdigit); + unicharset->set_ispunctuation(unichar_id, unichar_ispunct); + + tesseract::IcuErrorCode err; + unicharset->set_script(unichar_id, uscript_getName( + uscript_getScript(uni_vector[0], err))); + + const int num_code_points = uni_vector.size(); + // Obtain the lower/upper case if needed and record it in the properties. + unicharset->set_other_case(unichar_id, unichar_id); + if (unichar_islower || unichar_isupper) { + GenericVector other_case(num_code_points, 0); + for (int i = 0; i < num_code_points; ++i) { + // TODO(daria): Ideally u_strToLower()/ustrToUpper() should be used. + // However since they deal with UChars (so need a conversion function + // from char32 or UTF8string) and require a meaningful locale string, + // for now u_tolower()/u_toupper() are used. + other_case[i] = unichar_islower ? u_toupper(uni_vector[i]) : + u_tolower(uni_vector[i]); + } + STRING other_case_uch; + tesseract::UTF32ToUTF8(other_case, &other_case_uch); + UNICHAR_ID other_case_id = + unicharset->unichar_to_id(other_case_uch.c_str()); + if (other_case_id != INVALID_UNICHAR_ID) { + unicharset->set_other_case(unichar_id, other_case_id); + } else { + tprintf("Other case %s of %s is not in unicharset", + other_case_uch.c_str(), unichar_str); + } + } + + // Set RTL property and obtain mirror unichar ID from ICU. + GenericVector mirrors(num_code_points, 0); + for (int i = 0; i < num_code_points; ++i) { + mirrors[i] = u_charMirror(uni_vector[i]); + if (i == 0) { // set directionality to that of the 1st code point + unicharset->set_direction(unichar_id, + static_cast( + u_charDirection(uni_vector[i]))); + } + } + STRING mirror_uch; + tesseract::UTF32ToUTF8(mirrors, &mirror_uch); + UNICHAR_ID mirror_uch_id = unicharset->unichar_to_id(mirror_uch.c_str()); + if (mirror_uch_id != INVALID_UNICHAR_ID) { + unicharset->set_mirror(unichar_id, mirror_uch_id); + } else { + tprintf("Mirror %s of %s is not in unicharset\n", + mirror_uch.c_str(), unichar_str); + } + + // Record normalized version of this unichar. + STRING normed_str = tesseract::NormalizeUTF8String(unichar_str); + if (unichar_id != 0 && normed_str.length() > 0) { + unicharset->set_normed(unichar_id, normed_str.c_str()); + } else { + unicharset->set_normed(unichar_id, unichar_str); + } + } + unicharset->post_load_setup(); +} + +// Helper to set the properties for an input unicharset file, writes to the +// output file. If an appropriate script unicharset can be found in the +// script_dir directory, then the tops and bottoms are expanded using the +// script unicharset. +// If non-empty, xheight data for the fonts are written to the xheights_file. +static void SetPropertiesForInputFile(const string& script_dir, + const string& input_unicharset_file, + const string& output_unicharset_file, + const string& output_xheights_file) { + UNICHARSET unicharset; + + // Load the input unicharset + unicharset.load_from_file(input_unicharset_file.c_str()); + tprintf("Loaded unicharset of size %d from file %s", unicharset.size(), + input_unicharset_file.c_str()); + + // Set unichar properties + tprintf("Setting unichar properties"); + SetupBasicProperties(&unicharset); + string xheights_str; + for (int s = 0; s < unicharset.get_script_table_size(); ++s) { + // Load the unicharset for the script if available. + string filename = script_dir + "/" + + unicharset.get_script_from_script_id(s) + ".unicharset"; + UNICHARSET script_set; + if (script_set.load_from_file(filename.c_str())) { + unicharset.SetPropertiesFromOther(script_set); + } + // Load the xheights for the script if available. + filename = script_dir + "/" + unicharset.get_script_from_script_id(s) + + ".xheights"; + string script_heights; + if (File::ReadFileToString(filename, &script_heights)) + xheights_str += script_heights; + } + if (!output_xheights_file.empty()) + File::WriteStringToFileOrDie(xheights_str, output_xheights_file); + + // Write the output unicharset + tprintf("Writing unicharset to file %s", output_unicharset_file.c_str()); + unicharset.save_to_file(output_unicharset_file.c_str()); +} +} // namespace tesseract + + +int main(int argc, char** argv) { + tesseract::ParseCommandLineFlags(argv[0], &argc, &argv, true); + + // Check validity of input flags. + if (FLAGS_U.empty() || FLAGS_O.empty()) { + tprintf("Specify both input and output unicharsets!"); + exit(1); + } + if (FLAGS_script_dir.empty()) { + tprintf("Must specify a script_dir!"); + exit(1); + } + + tesseract::SetPropertiesForInputFile(FLAGS_script_dir.c_str(), + FLAGS_U.c_str(), FLAGS_O.c_str(), + FLAGS_X.c_str()); + return 0; +} diff --git a/training/shapeclustering.cpp b/training/shapeclustering.cpp index c28104e192..18428d2d75 100644 --- a/training/shapeclustering.cpp +++ b/training/shapeclustering.cpp @@ -56,7 +56,7 @@ int main(int argc, char **argv) { return 1; if (FLAGS_display_cloud_font >= 0) { - #ifndef GRAPHICS_DISABLED + #ifndef GRAPHICS_DISABLED trainer->DisplaySamples(FLAGS_canonical_class1.c_str(), FLAGS_display_cloud_font, FLAGS_canonical_class2.c_str(), diff --git a/training/unicharset_extractor.cpp b/training/unicharset_extractor.cpp index 031b081799..46e7364107 100644 --- a/training/unicharset_extractor.cpp +++ b/training/unicharset_extractor.cpp @@ -103,11 +103,10 @@ int main(int argc, char** argv) { int option; const char* output_directory = "."; STRING unicharset_file_name; + // Special characters are now included by default. UNICHARSET unicharset; setlocale(LC_ALL, ""); - // Space character needed to represent NIL classification - unicharset.unichar_insert(" "); // Print usage if (argc <= 1) { diff --git a/training/wordlist2dawg.cpp b/training/wordlist2dawg.cpp index 3e63d9c389..17fef5a568 100644 --- a/training/wordlist2dawg.cpp +++ b/training/wordlist2dawg.cpp @@ -35,15 +35,9 @@ static const int kMaxNumEdges = 30000000; int main(int argc, char** argv) { - int min_word_length; - int max_word_length; if (!(argc == 4 || (argc == 5 && strcmp(argv[1], "-t") == 0) || - (argc == 6 && strcmp(argv[1], "-r") == 0) || - (argc == 7 && strcmp(argv[1], "-l") == 0 && - sscanf(argv[2], "%d", &min_word_length) == 1 && - sscanf(argv[3], "%d", &max_word_length) == 1))) { - printf("Usage: %s [-t | -r [reverse policy] |" - " -l min_len max_len] word_list_file" + (argc == 6 && strcmp(argv[1], "-r") == 0))) { + printf("Usage: %s [-t | -r [reverse policy] ] word_list_file" " dawg_file unicharset_file\n", argv[0]); return 1; } @@ -78,8 +72,9 @@ int main(int argc, char** argv) { kMaxNumEdges, unicharset.size(), classify->getDict().dawg_debug_level); tprintf("Reading word list from '%s'\n", wordlist_filename); - if (!trie.read_word_list(wordlist_filename, unicharset, reverse_policy)) { - tprintf("Failed to read word list from '%s'\n", wordlist_filename); + if (!trie.read_and_add_word_list(wordlist_filename, unicharset, + reverse_policy)) { + tprintf("Failed to add word list from '%s'\n", wordlist_filename); exit(1); } tprintf("Reducing Trie to SquishedDawg\n"); @@ -100,76 +95,6 @@ int main(int argc, char** argv) { classify->getDict().dawg_debug_level); tprintf("Checking word list from '%s'\n", wordlist_filename); words.check_for_words(wordlist_filename, unicharset, true); - } else if (argc == 7) { - // Place words of different lengths in separate Dawgs. - char str[CHARS_PER_LINE]; - FILE *word_file = fopen(wordlist_filename, "rb"); - if (word_file == NULL) { - tprintf("Failed to open wordlist file %s\n", wordlist_filename); - exit(1); - } - FILE *dawg_file = fopen(dawg_filename, "wb"); - if (dawg_file == NULL) { - tprintf("Failed to open dawg output file %s\n", dawg_filename); - exit(1); - } - tprintf("Reading word list from '%s'\n", wordlist_filename); - GenericVector trie_vec; - int i; - for (i = min_word_length; i <= max_word_length; ++i) { - trie_vec.push_back(new tesseract::Trie( - // the first 3 arguments are not used in this case - tesseract::DAWG_TYPE_WORD, "", SYSTEM_DAWG_PERM, - kMaxNumEdges, unicharset.size(), - classify->getDict().dawg_debug_level)); - } - while (fgets(str, CHARS_PER_LINE, word_file) != NULL) { - chomp_string(str); // remove newline - int badpos; - if (!unicharset.encodable_string(str, &badpos)) { - tprintf("String '%s' not compatible with unicharset. " - "Bad chars here: '%s'\n", str, str + badpos); - continue; - } - WERD_CHOICE word(str, unicharset); - if ((reverse_policy == tesseract::Trie::RRP_REVERSE_IF_HAS_RTL && - word.has_rtl_unichar_id()) || - reverse_policy == tesseract::Trie::RRP_FORCE_REVERSE) { - word.reverse_and_mirror_unichar_ids(); - } - if (word.length() >= min_word_length && - word.length() <= max_word_length && - !word.contains_unichar_id(INVALID_UNICHAR_ID)) { - tesseract::Trie *curr_trie = trie_vec[word.length()-min_word_length]; - if (!curr_trie->word_in_dawg(word)) { - if (!curr_trie->add_word_to_dawg(word)) { - tprintf("Failed to add the following word to dawg:\n"); - word.print(); - exit(1); - } - if (classify->getDict().dawg_debug_level > 1) { - tprintf("Added word %s of length %d\n", str, word.length()); - } - if (!curr_trie->word_in_dawg(word)) { - tprintf("Error: word '%s' not in DAWG after adding it\n", str); - exit(1); - } - } - } - } - fclose(word_file); - tprintf("Writing fixed length dawgs to '%s'\n", dawg_filename); - GenericVector dawg_vec; - for (i = 0; i <= max_word_length; ++i) { - dawg_vec.push_back(i < min_word_length ? NULL : - trie_vec[i-min_word_length]->trie_to_dawg()); - } - tesseract::Dict::WriteFixedLengthDawgs( - dawg_vec, max_word_length - min_word_length + 1, - classify->getDict().dawg_debug_level, dawg_file); - fclose(dawg_file); - dawg_vec.delete_data_pointers(); - trie_vec.delete_data_pointers(); } else { // should never get here tprintf("Invalid command-line options\n"); exit(1); diff --git a/viewer/svutil.cpp b/viewer/svutil.cpp index 2017565948..46974fec34 100644 --- a/viewer/svutil.cpp +++ b/viewer/svutil.cpp @@ -125,14 +125,14 @@ void SVSync::StartProcess(const char* executable, const char* args) { SVSemaphore::SVSemaphore() { #ifdef _WIN32 semaphore_ = CreateSemaphore(0, 0, 10, 0); -#elif defined (__APPLE__) +#elif defined(__APPLE__) char name[50]; - sprintf(name, "%d", random()); + snprintf(name, sizeof(name), "%d", random()); sem_unlink(name); semaphore_ = sem_open(name, O_CREAT , S_IWUSR, 0); if (semaphore_ == SEM_FAILED) { - perror("sem_open"); - } + perror("sem_open"); + } #else sem_init(&semaphore_, 0, 0); #endif @@ -141,7 +141,7 @@ SVSemaphore::SVSemaphore() { void SVSemaphore::Signal() { #ifdef _WIN32 ReleaseSemaphore(semaphore_, 1, NULL); -#elif defined (__APPLE__) +#elif defined(__APPLE__) sem_post(semaphore_); #else sem_post(&semaphore_); @@ -151,7 +151,7 @@ void SVSemaphore::Signal() { void SVSemaphore::Wait() { #ifdef _WIN32 WaitForSingleObject(semaphore_, INFINITE); -#elif defined (__APPLE__) +#elif defined(__APPLE__) sem_wait(semaphore_); #else sem_wait(&semaphore_); @@ -436,7 +436,6 @@ SVNetwork::SVNetwork(const char* hostname, int port) { stream_ = socket(addr_info->ai_family, addr_info->ai_socktype, addr_info->ai_protocol); - } } FreeAddrInfo(addr_info); diff --git a/viewer/svutil.h b/viewer/svutil.h index 847bf3befe..0d600ede57 100644 --- a/viewer/svutil.h +++ b/viewer/svutil.h @@ -75,7 +75,7 @@ class SVSemaphore { private: #ifdef _WIN32 HANDLE semaphore_; -#elif defined (__APPLE__) +#elif defined(__APPLE__) sem_t *semaphore_; #else sem_t semaphore_; diff --git a/wordrec/Makefile.am b/wordrec/Makefile.am index 34b4b49480..fcea7ecea4 100644 --- a/wordrec/Makefile.am +++ b/wordrec/Makefile.am @@ -10,12 +10,13 @@ AM_CPPFLAGS += -DTESS_EXPORTS \ endif noinst_HEADERS = \ - associate.h bestfirst.h chop.h \ - chopper.h closed.h drawfx.h findseam.h gradechop.h \ - language_model.h makechop.h matchtab.h measure.h \ - olutil.h outlines.h plotedges.h \ - plotseg.h render.h \ - wordclass.h wordrec.h + associate.h chop.h \ + chopper.h drawfx.h findseam.h gradechop.h \ + language_model.h lm_consistency.h lm_pain_points.h lm_state.h \ + makechop.h measure.h \ + olutil.h outlines.h params_model.h plotedges.h \ + render.h \ + wordrec.h if !USING_MULTIPLELIBS noinst_LTLIBRARIES = libtesseract_wordrec.la @@ -33,9 +34,10 @@ libtesseract_wordrec_la_LIBADD = \ endif libtesseract_wordrec_la_SOURCES = \ - associate.cpp bestfirst.cpp chop.cpp chopper.cpp \ - closed.cpp drawfx.cpp findseam.cpp gradechop.cpp \ - heuristic.cpp language_model.cpp makechop.cpp matchtab.cpp \ - olutil.cpp outlines.cpp pieces.cpp \ - plotedges.cpp plotseg.cpp render.cpp segsearch.cpp \ + associate.cpp chop.cpp chopper.cpp \ + drawfx.cpp findseam.cpp gradechop.cpp \ + language_model.cpp lm_consistency.cpp lm_pain_points.cpp lm_state.cpp \ + makechop.cpp \ + olutil.cpp outlines.cpp params_model.cpp pieces.cpp \ + plotedges.cpp render.cpp segsearch.cpp \ tface.cpp wordclass.cpp wordrec.cpp diff --git a/wordrec/associate.cpp b/wordrec/associate.cpp index 7cf799bf3c..aefebb5166 100644 --- a/wordrec/associate.cpp +++ b/wordrec/associate.cpp @@ -26,8 +26,8 @@ #include #include "associate.h" -#include "baseline.h" #include "normalis.h" +#include "pageres.h" namespace tesseract { @@ -39,70 +39,86 @@ void AssociateUtils::ComputeStats(int col, int row, int parent_path_length, bool fixed_pitch, float max_char_wh_ratio, - const DENORM *denorm, - CHUNKS_RECORD *chunks_record, - int debug_level, + WERD_RES *word_res, + bool debug, AssociateStats *stats) { stats->Clear(); - if (debug_level > 0) { + ASSERT_HOST(word_res != NULL); + if (word_res->blob_widths.empty()) { + return; + } + if (debug) { tprintf("AssociateUtils::ComputeStats() for col=%d, row=%d%s\n", col, row, fixed_pitch ? " (fixed pitch)" : ""); } - float normalizing_height = BASELINE_SCALE; + float normalizing_height = kBlnXHeight; + ROW* blob_row = word_res->blob_row; // TODO(rays/daria) Can unicharset.script_has_xheight be useful here? - if (fixed_pitch && denorm != NULL && denorm->row() != NULL) { + if (fixed_pitch && blob_row != NULL) { // For fixed pitch language like CJK, we use the full text height // as the normalizing factor so we are not dependent on xheight // calculation. - if (denorm->row()->body_size() > 0.0f) { - normalizing_height = denorm->y_scale() * denorm->row()->body_size(); + if (blob_row->body_size() > 0.0f) { + normalizing_height = word_res->denorm.y_scale() * blob_row->body_size(); } else { - normalizing_height = denorm->y_scale() * - (denorm->row()->x_height() + denorm->row()->ascenders()); + normalizing_height = word_res->denorm.y_scale() * + (blob_row->x_height() + blob_row->ascenders()); } - if (debug_level > 0) { + if (debug) { tprintf("normalizing height = %g (scale %g xheight %g ascenders %g)\n", - normalizing_height, denorm->y_scale(), denorm->row()->x_height(), - denorm->row()->ascenders()); + normalizing_height, word_res->denorm.y_scale(), + blob_row->x_height(), blob_row->ascenders()); } } - float wh_ratio = - GetChunksWidth(chunks_record->chunk_widths, col, row) / normalizing_height; - if (debug_level) tprintf("wh_ratio %g\n", wh_ratio); + float wh_ratio = word_res->GetBlobsWidth(col, row) / normalizing_height; if (wh_ratio > max_char_wh_ratio) stats->bad_shape = true; + // Compute the gap sum for this shape. If there are only negative or only + // positive gaps, record their sum in stats->gap_sum. However, if there is + // a mixture, record only the sum of the positive gaps. + // TODO(antonova): explain fragment. + int negative_gap_sum = 0; + for (int c = col; c < row; ++c) { + int gap = word_res->GetBlobsGap(c); + (gap > 0) ? stats->gap_sum += gap : negative_gap_sum += gap; + } + if (stats->gap_sum == 0) stats->gap_sum = negative_gap_sum; + if (debug) { + tprintf("wh_ratio=%g (max_char_wh_ratio=%g) gap_sum=%d %s\n", + wh_ratio, max_char_wh_ratio, stats->gap_sum, + stats->bad_shape ? "bad_shape" : ""); + } + // Compute shape_cost (for fixed pitch mode). if (fixed_pitch) { - bool end_row = (row == (chunks_record->ratings->dimension() - 1)); + bool end_row = (row == (word_res->ratings->dimension() - 1)); // Ensure that the blob has gaps on the left and the right sides // (except for beginning and ending punctuation) and that there is // no cutting through ink at the blob boundaries. if (col > 0) { - float left_gap = - GetChunksGap(chunks_record->chunk_widths, col-1) / normalizing_height; - SEAM *left_seam = - static_cast(array_value(chunks_record->splits, col-1)); - if (debug_level) { - tprintf("left_gap %g, left_seam %g\n", left_gap, left_seam->priority); - } + float left_gap = word_res->GetBlobsGap(col - 1) / normalizing_height; + SEAM *left_seam = word_res->seam_array[col - 1]; if ((!end_row && left_gap < kMinGap) || left_seam->priority > 0.0f) { stats->bad_shape = true; } + if (debug) { + tprintf("left_gap %g, left_seam %g %s\n", left_gap, left_seam->priority, + stats->bad_shape ? "bad_shape" : ""); + } } float right_gap = 0.0f; if (!end_row) { - right_gap = - GetChunksGap(chunks_record->chunk_widths, row) / normalizing_height; - SEAM *right_seam = - static_cast(array_value(chunks_record->splits, row)); - if (debug_level) { - tprintf("right_gap %g right_seam %g\n", - right_gap, right_seam->priority); - } + right_gap = word_res->GetBlobsGap(row) / normalizing_height; + SEAM *right_seam = word_res->seam_array[row]; if (right_gap < kMinGap || right_seam->priority > 0.0f) { stats->bad_shape = true; if (right_gap < kMinGap) stats->bad_fixed_pitch_right_gap = true; } + if (debug) { + tprintf("right_gap %g right_seam %g %s\n", + right_gap, right_seam->priority, + stats->bad_shape ? "bad_shape" : ""); + } } // Impose additional segmentation penalties if blob widths or gaps @@ -121,7 +137,7 @@ void AssociateUtils::ComputeStats(int col, int row, } else { stats->full_wh_ratio_total = stats->full_wh_ratio; } - if (debug_level) { + if (debug) { tprintf("full_wh_ratio %g full_wh_ratio_total %g full_wh_ratio_var %g\n", stats->full_wh_ratio, stats->full_wh_ratio_total, stats->full_wh_ratio_var); @@ -137,18 +153,10 @@ void AssociateUtils::ComputeStats(int col, int row, stats->shape_cost += 10; } stats->shape_cost += stats->full_wh_ratio_var; - if (debug_level) tprintf("shape_cost %g\n", stats->shape_cost); + if (debug) tprintf("shape_cost %g\n", stats->shape_cost); } } -int AssociateUtils::GetChunksWidth(WIDTH_RECORD *width_record, - int start_blob, int last_blob) { - int result = 0; - for (int x = start_blob * 2; x <= last_blob * 2; x++) - result += width_record->widths[x]; - return result; -} - float AssociateUtils::FixedPitchWidthCost(float norm_width, float right_gap, bool end_pos, diff --git a/wordrec/associate.h b/wordrec/associate.h index 02d4461a41..977193b21c 100644 --- a/wordrec/associate.h +++ b/wordrec/associate.h @@ -24,41 +24,12 @@ #include "blobs.h" #include "elst.h" -#include "matrix.h" +#include "ratngs.h" #include "seam.h" #include "split.h" -#include "states.h" class WERD_RES; -typedef inT16 BLOB_WEIGHTS[MAX_NUM_CHUNKS]; - -// Each unichar evaluated. -struct EVALUATION_RECORD { - float match; - float certainty; - char character; - int width; - int gap; -}; - -typedef EVALUATION_RECORD EVALUATION_ARRAY[MAX_NUM_CHUNKS]; - -// Classification info for chunks. -// -// TODO(daria): move to tesseract namespace when obsolete code using -// this struct that is not in tesseract namespace is deprecated. -struct CHUNKS_RECORD { - MATRIX *ratings; - TBLOB *chunks; - WERD_RES* word_res; // Borrowed pointer - do not delete! - SEAMS splits; - int x_height; - WIDTH_RECORD *chunk_widths; - WIDTH_RECORD *char_widths; - inT16 *weights; -}; - namespace tesseract { // Statisitcs about character widths, gaps and seams. @@ -73,6 +44,7 @@ struct AssociateStats { full_wh_ratio_var = 0.0f; bad_fixed_pitch_right_gap = false; bad_fixed_pitch_wh_ratio = false; + gap_sum = 0; } void Print() { @@ -89,6 +61,7 @@ struct AssociateStats { // the blob on the right bool bad_fixed_pitch_wh_ratio; // true if the blobs has width-to-hight // ratio > kMaxFixedPitchCharAspectRatio + int gap_sum; // sum of gaps within the blob }; // Utility functions for scoring segmentation paths according to their @@ -98,9 +71,26 @@ class AssociateUtils { static const float kMaxFixedPitchCharAspectRatio; static const float kMinGap; + // Returns outline length of the given blob is computed as: + // rating_cert_scale * rating / certainty + // Since from Wordrec::SegSearch() in segsearch.cpp + // rating_cert_scale = -1.0 * getDict().certainty_scale / rating_scale + // And from Classify::ConvertMatchesToChoices() in adaptmatch.cpp + // Rating = Certainty = next.rating + // Rating *= rating_scale * Results->BlobLength + // Certainty *= -(getDict().certainty_scale) + static inline float ComputeOutlineLength(float rating_cert_scale, + const BLOB_CHOICE &b) { + return rating_cert_scale * b.rating() / b.certainty(); + } + static inline float ComputeRating(float rating_cert_scale, + float cert, int width) { + return static_cast(width) * cert / rating_cert_scale; + } + // Computes character widths, gaps and seams stats given the // AssociateStats of the path so far, col, row of the blob that - // is being added to the path, and CHUNKS_RECORD containing information + // is being added to the path, and WERD_RES containing information // about character widths, gaps and seams. // Fills associate_cost with the combined shape, gap and seam cost // of adding a unichar from (col, row) to the path (note that since @@ -108,32 +98,17 @@ class AssociateUtils { // pain points, (col, row) entry might not be classified yet; thus // information in the (col, row) entry of the ratings matrix is not used). // - // Note: the function assumes that chunks_record, stats and + // Note: the function assumes that word_res, stats and // associate_cost pointers are not NULL. static void ComputeStats(int col, int row, const AssociateStats *parent_stats, int parent_path_length, bool fixed_pitch, float max_char_wh_ratio, - const DENORM *denorm, - CHUNKS_RECORD *chunks_record, - int debug_level, + WERD_RES *word_res, + bool debug, AssociateStats *stats); - // Returns the width of a chunk which is a composed of several blobs - // blobs[start_blob..last_blob] inclusively. - // Widths/gaps records are in the form: - // width_record->num_char = n - // width_record->widths[2*n-1] = w0,g0,w1,g1..w(n-1),g(n-1) - static int GetChunksWidth(WIDTH_RECORD *width_record, - int start_blob, int last_blob); - - // Returns the width of a gap between the specified chunk and the next one. - static inline int GetChunksGap(WIDTH_RECORD *width_record, int last_chunk) { - return (last_chunk >= 0 && last_chunk < width_record->num_chars - 1) ? - width_record->widths[last_chunk * 2 + 1] : 0; - } - // Returns the width cost for fixed-pitch text. static float FixedPitchWidthCost(float norm_width, float right_gap, bool end_pos, float max_char_wh_ratio); diff --git a/wordrec/bestfirst.cpp b/wordrec/bestfirst.cpp deleted file mode 100644 index 9833c1800b..0000000000 --- a/wordrec/bestfirst.cpp +++ /dev/null @@ -1,772 +0,0 @@ -/* -*-C-*- - ******************************************************************************** - * - * File: bestfirst.c (Formerly bestfirst.c) - * Description: Best first search functions - * Author: Mark Seaman, OCR Technology - * Created: Mon May 14 11:23:29 1990 - * Modified: Tue Jul 30 16:08:47 1991 (Mark Seaman) marks@hpgrlt - * Language: C - * Package: N/A - * Status: Experimental (Do Not Distribute) - * - * (c) Copyright 1990, Hewlett-Packard Company. - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** http://www.apache.org/licenses/LICENSE-2.0 - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - * - ***************************************************************************/ - -/*---------------------------------------------------------------------- - I n c l u d e s ----------------------------------------------------------------------*/ - -#include - -#include "associate.h" -#include "bestfirst.h" -#include "baseline.h" -#include "bitvec.h" -#include "dict.h" -#include "freelist.h" -#include "globals.h" -#include "helpers.h" -#include "pageres.h" -#include "permute.h" -#include "plotseg.h" -#include "ratngs.h" -#include "states.h" -#include "stopper.h" -#include "structures.h" -#include "unicharset.h" -#include "wordclass.h" -#include "wordrec.h" - -// Include automatically generated configuration file if running autoconf. -#ifdef HAVE_CONFIG_H -#include "config_auto.h" -#endif - -void call_caller(); - -static void log_state(const char * message, - int num_joints, - STATE *state) { - STRING segstate; - print_state(state, num_joints, &segstate); - tprintf("%20s [%40s]\n", message, segstate.string()); -} - -static void log_state(const char * message, - int num_joints, - STATE *state, - float priority) { - STRING segstate; - print_state(state, num_joints, &segstate); - tprintf("%20s [%40s], priority %8.3f\n", message, - segstate.string(), priority); -} - -/**/ -/*---------------------------------------------------------------------- - F u n c t i o n s -----------------------------------------------------------------------*/ - -namespace tesseract { -/** - * @name best_first_search - * - * Find the best segmentation by doing a best first search of the - * solution space. - */ -void Wordrec::best_first_search(CHUNKS_RECORD *chunks_record, - BLOB_CHOICE_LIST_VECTOR *best_char_choices, - WERD_RES *word, - STATE *state, - DANGERR *fixpt, - STATE *best_state) { - SEARCH_RECORD *the_search; - inT16 keep_going; - STATE guided_state; // not used - - int num_joints = chunks_record->ratings->dimension() - 1; - the_search = new_search(chunks_record, num_joints, best_char_choices, - word->best_choice, word->raw_choice, state); - - // The default state is initialized as the best choice. In order to apply - // segmentation adjustment, or any other contextual processing in permute, - // we give the best choice a poor rating to force the processed raw choice - // to be promoted to best choice. - the_search->best_choice->set_rating(WERD_CHOICE::kBadRating); - evaluate_state(chunks_record, the_search, fixpt, word->blamer_bundle); - if (wordrec_debug_level > 1) { - tprintf("\n\n\n =========== BestFirstSearch ==============\n"); - word->best_choice->print("**Initial BestChoice**"); - } - - FLOAT32 worst_priority = 2.0f * prioritize_state(chunks_record, the_search); - if (worst_priority < wordrec_worst_state) - worst_priority = wordrec_worst_state; - if (wordrec_debug_level > 1) { - log_state("BestFirstSearch", num_joints, best_state); - } - - guided_state = *state; - do { - /* Look for answer */ - STATE orig_state = *the_search->this_state; - if (!hash_lookup (the_search->closed_states, the_search->this_state)) { - guided_state = *(the_search->this_state); - keep_going = evaluate_state(chunks_record, the_search, fixpt, - word->blamer_bundle); - hash_add (the_search->closed_states, the_search->this_state); - - if (!keep_going || - (the_search->num_states > wordrec_num_seg_states)) { - if (wordrec_debug_level > 1) - tprintf("Breaking best_first_search on keep_going %s numstates %d\n", - ((keep_going) ? "T" :"F"), the_search->num_states); - free_state (the_search->this_state); - break; - } - - FLOAT32 new_worst_priority = 2.0f * prioritize_state(chunks_record, - the_search); - if (new_worst_priority < worst_priority) { - if (wordrec_debug_level > 1) - tprintf("Lowering WorstPriority %f --> %f\n", - worst_priority, new_worst_priority); - // Tighten the threshold for admitting new paths as better search - // candidates are found. After lowering this threshold, we can safely - // popout everything that is worse than this score also. - worst_priority = new_worst_priority; - } - expand_node(worst_priority, chunks_record, the_search); - } - - if (wordrec_debug_level > 1) { - log_state("Done with", the_search->num_joints, &orig_state); - } - free_state (the_search->this_state); - num_popped++; - the_search->this_state = pop_queue (the_search->open_states); - if (wordrec_debug_level > 1 && !the_search->this_state) - tprintf("No more states to evalaute after %d evals", num_popped); - } while (the_search->this_state); - - state->part1 = the_search->best_state->part1; - state->part2 = the_search->best_state->part2; - if (wordrec_debug_level > 1) { - tprintf("\n\n\n =========== BestFirstSearch ==============\n"); - // best_choice->debug_string().string()); - word->best_choice->print("**Final BestChoice**"); - } - // save the best_state stats - delete_search(the_search); -} - -/** - * delete_search - * - * Terminate the current search and free all the memory involved. - */ -void Wordrec::delete_search(SEARCH_RECORD *the_search) { - float closeness; - - closeness = (the_search->num_joints ? - (hamming_distance(reinterpret_cast(the_search->first_state), - reinterpret_cast(the_search->best_state), 2) / - (float) the_search->num_joints) : 0.0f); - - free_state (the_search->first_state); - free_state (the_search->best_state); - - free_hash_table(the_search->closed_states); - FreeHeapData (the_search->open_states, (void_dest) free_state); - - memfree(the_search); -} - - -/** - * evaluate_chunks - * - * A particular word level segmentation has been chosen. Evaluation - * this to find the word list that corresponds to it. - */ -BLOB_CHOICE_LIST_VECTOR *Wordrec::evaluate_chunks(CHUNKS_RECORD *chunks_record, - SEARCH_STATE search_state, - BlamerBundle *blamer_bundle) { - BLOB_CHOICE_LIST_VECTOR *char_choices = new BLOB_CHOICE_LIST_VECTOR(); - BLOB_CHOICE_LIST *blob_choices; - BLOB_CHOICE_IT blob_choice_it; - int i; - int x = 0; - int y; - - // Iterate sub-paths. - for (i = 1; i <= search_state[0] + 1; i++) { - if (i > search_state[0]) - y = count_blobs (chunks_record->chunks) - 1; - else - y = x + search_state[i]; - - // Process one square. - - // Classify if needed. - blob_choices = get_piece_rating(chunks_record->ratings, - chunks_record->chunks, - chunks_record->word_res->denorm, - chunks_record->splits, - x, y, blamer_bundle); - - if (blob_choices == NULL) { - delete char_choices; - return (NULL); - } - - // Add permuted ratings. - blob_choice_it.set_to_list(blob_choices); - last_segmentation[i - 1].certainty = blob_choice_it.data()->certainty(); - last_segmentation[i - 1].match = blob_choice_it.data()->rating(); - - last_segmentation[i - 1].width = - AssociateUtils::GetChunksWidth(chunks_record->chunk_widths, x, y); - last_segmentation[i - 1].gap = - AssociateUtils::GetChunksGap(chunks_record->chunk_widths, y); - - *char_choices += blob_choices; - x = y + 1; - } - return (char_choices); -} - -/** - * @name evaluate_state - * - * Evaluate the segmentation that is represented by this state in the - * best first search. Add this state to the "states_seen" list. - */ -inT16 Wordrec::evaluate_state(CHUNKS_RECORD *chunks_record, - SEARCH_RECORD *the_search, - DANGERR *fixpt, - BlamerBundle *blamer_bundle) { - BLOB_CHOICE_LIST_VECTOR *char_choices; - SEARCH_STATE chunk_groups; - float rating_limit = the_search->best_choice->rating(); - bool keep_going = true; - PIECES_STATE widths; - - the_search->num_states++; - chunk_groups = bin_to_chunks(the_search->this_state, - the_search->num_joints); - bin_to_pieces (the_search->this_state, the_search->num_joints, widths); - if (wordrec_debug_level > 1) { - log_state("Evaluating state", the_search->num_joints, - the_search->this_state); - } - getDict().LogNewSegmentation(widths); - - char_choices = evaluate_chunks(chunks_record, chunk_groups, blamer_bundle); - getDict().SetWordsegRatingAdjustFactor(-1.0f); - bool updated_best_choice = false; - if (char_choices != NULL && char_choices->length() > 0) { - // Compute the segmentation cost and include the cost in word rating. - // TODO(dsl): We should change the SEARCH_RECORD to store this cost - // from state evaluation and avoid recomputing it here. - prioritize_state(chunks_record, the_search); - getDict().SetWordsegRatingAdjustFactor(the_search->segcost_bias); - updated_best_choice = - getDict().permute_characters(*char_choices, - the_search->best_choice, - the_search->raw_choice); - bool replaced = false; - if (updated_best_choice) { - if (getDict().AcceptableChoice(char_choices, the_search->best_choice, - NULL, ASSOCIATOR_CALLER, &replaced)) { - keep_going = false; - } - CopyCharChoices(*char_choices, the_search->best_char_choices); - } - } - getDict().SetWordsegRatingAdjustFactor(-1.0f); - -#ifndef GRAPHICS_DISABLED - if (wordrec_display_segmentations) { - display_segmentation (chunks_record->chunks, chunk_groups); - if (wordrec_display_segmentations > 1) - window_wait(segm_window); - } -#endif - - if (rating_limit != the_search->best_choice->rating()) { - ASSERT_HOST(updated_best_choice); - the_search->before_best = the_search->num_states; - the_search->best_state->part1 = the_search->this_state->part1; - the_search->best_state->part2 = the_search->this_state->part2; - replace_char_widths(chunks_record, chunk_groups); - } else { - ASSERT_HOST(!updated_best_choice); - if (char_choices != NULL) fixpt->clear(); - } - - if (char_choices != NULL) delete char_choices; - memfree(chunk_groups); - - return (keep_going); -} - - -/** - * rebuild_current_state - * - * Transfers the given state to the word's output fields: rebuild_word, - * best_state, box_word, and returns the corresponding blob choices. - */ -BLOB_CHOICE_LIST_VECTOR *Wordrec::rebuild_current_state( - WERD_RES *word, - STATE *state, - BLOB_CHOICE_LIST_VECTOR *old_choices, - MATRIX *ratings) { - // Initialize search_state, num_joints, x, y. - int num_joints = array_count(word->seam_array); -#ifndef GRAPHICS_DISABLED - if (wordrec_display_segmentations) { - print_state("Rebuilding state", state, num_joints); - } -#endif - // Setup the rebuild_word ready for the output blobs. - if (word->rebuild_word != NULL) - delete word->rebuild_word; - word->rebuild_word = new TWERD; - // Setup the best_state. - word->best_state.clear(); - SEARCH_STATE search_state = bin_to_chunks(state, num_joints); - // See which index is which below for information on x and y. - int x = 0; - int y; - for (int i = 1; i <= search_state[0]; i++) { - y = x + search_state[i]; - x = y + 1; - } - y = count_blobs(word->chopped_word->blobs) - 1; - - // Initialize char_choices, expanded_fragment_lengths: - // e.g. if fragment_lengths = {1 1 2 3 1}, - // expanded_fragment_lengths_str = {1 1 2 2 3 3 3 1}. - BLOB_CHOICE_LIST_VECTOR *char_choices = new BLOB_CHOICE_LIST_VECTOR(); - STRING expanded_fragment_lengths_str = ""; - bool state_has_fragments = false; - const char *fragment_lengths = NULL; - - if (word->best_choice->length() > 0) { - fragment_lengths = word->best_choice->fragment_lengths(); - } - if (fragment_lengths) { - for (int i = 0; i < word->best_choice->length(); ++i) { - *char_choices += NULL; - word->best_state.push_back(0); - if (fragment_lengths[i] > 1) { - state_has_fragments = true; - } - for (int j = 0; j < fragment_lengths[i]; ++j) { - expanded_fragment_lengths_str += fragment_lengths[i]; - } - } - } else { - for (int i = 0; i <= search_state[0]; ++i) { - expanded_fragment_lengths_str += (char)1; - *char_choices += NULL; - word->best_state.push_back(0); - } - } - - // Set up variables for concatenating fragments. - const char *word_lengths_ptr = NULL; - const char *word_ptr = NULL; - if (state_has_fragments) { - // Make word_lengths_ptr point to the last element in - // best_choice->unichar_lengths(). - word_lengths_ptr = word->best_choice->unichar_lengths().string(); - word_lengths_ptr += (strlen(word_lengths_ptr)-1); - // Make word_str point to the beginning of the last - // unichar in best_choice->unichar_string(). - word_ptr = word->best_choice->unichar_string().string(); - word_ptr += (strlen(word_ptr)-*word_lengths_ptr); - } - const char *expanded_fragment_lengths = - expanded_fragment_lengths_str.string(); - char unichar[UNICHAR_LEN + 1]; - - // Populate char_choices list such that it corresponds to search_state. - // - // If we are rebuilding a state that contains character fragments: - // -- combine blobs that belong to character fragments - // -- re-classify the blobs to obtain choices list for the merged blob - // -- ensure that correct classification appears in the new choices list - // NOTE: a choice composed form original fragment choices will be always - // added to the new choices list for each character composed from - // fragments (even if the choice for the corresponding character appears - // in the re-classified choices list of for the newly merged blob). - int ss_index = search_state[0]; - // Which index is which? - // char_choices_index refers to the finished product: there is one for each - // blob/unicharset entry in the final word. - // ss_index refers to the search_state, and indexes a group (chunk) of blobs - // that were classified together for the best state. - // old_choice_index is a copy of ss_index, and accesses the old_choices, - // which correspond to chunks in the best state. old_choice_index gets - // set to -1 on a fragment set, as there is no corresponding chunk in - // the best state. - // x and y refer to the underlying blobs and are the first and last blob - // indices in a chunk. - for (int char_choices_index = char_choices->length() - 1; - char_choices_index >= 0; - --char_choices_index) { - // The start and end of the blob to rebuild. - int true_x = x; - int true_y = y; - // The fake merged fragment choice. - BLOB_CHOICE* merged_choice = NULL; - // Test for and combine fragments first. - int fragment_pieces = expanded_fragment_lengths[ss_index]; - int old_choice_index = ss_index; - - if (fragment_pieces > 1) { - strncpy(unichar, word_ptr, *word_lengths_ptr); - unichar[*word_lengths_ptr] = '\0'; - merged_choice = rebuild_fragments(unichar, expanded_fragment_lengths, - old_choice_index, old_choices); - old_choice_index = -1; - } - while (fragment_pieces > 0) { - true_x = x; - // Move left to the previous blob. - y = x - 1; - x = y - search_state[ss_index--]; - --fragment_pieces; - } - word->best_state[char_choices_index] = true_y + 1 - true_x; - BLOB_CHOICE_LIST *current_choices = join_blobs_and_classify( - word, true_x, true_y, old_choice_index, ratings, old_choices); - if (merged_choice != NULL) { - // Insert merged_blob into current_choices, such that current_choices - // are still sorted in non-descending order by rating. - ASSERT_HOST(!current_choices->empty()); - BLOB_CHOICE_IT choice_it(current_choices); - for (choice_it.mark_cycle_pt(); !choice_it.cycled_list() && - merged_choice->rating() > choice_it.data()->rating(); - choice_it.forward()) - choice_it.add_before_stay_put(merged_choice); - } - // Get rid of fragments in current_choices. - BLOB_CHOICE_IT choice_it(current_choices); - for (choice_it.mark_cycle_pt(); !choice_it.cycled_list(); - choice_it.forward()) { - if (getDict().getUnicharset().get_fragment( - choice_it.data()->unichar_id())) { - delete choice_it.extract(); - } - } - char_choices->set(current_choices, char_choices_index); - - // Update word_ptr and word_lengths_ptr. - if (word_lengths_ptr != NULL && word_ptr != NULL) { - word_lengths_ptr--; - word_ptr -= (*word_lengths_ptr); - } - } - old_choices->delete_data_pointers(); - delete old_choices; - memfree(search_state); - - return char_choices; -} - -/** - * @name expand_node - * - * Create the states that are attached to this one. Check to see that - * each one has not already been visited. If not add it to the priority - * queue. - */ -void Wordrec::expand_node(FLOAT32 worst_priority, - CHUNKS_RECORD *chunks_record, - SEARCH_RECORD *the_search) { - STATE old_state; - int x; - uinT32 mask = 1 << (the_search->num_joints - 1 - 32); - - old_state.part1 = the_search->this_state->part1; - old_state.part2 = the_search->this_state->part2; - - // We need to expand the search more intelligently, or we get stuck - // with a bad starting segmentation in a long word sequence as in CJK. - // Expand a child node only if it is within the global bound, and no - // worse than 2x of its parent. - // TODO(dsl): There is some redudency here in recomputing the priority, - // and in filtering of old_merit and worst_priority. - the_search->this_state->part2 = old_state.part2; - for (x = the_search->num_joints; x > 32; x--) { - the_search->this_state->part1 = mask ^ old_state.part1; - if (!hash_lookup (the_search->closed_states, the_search->this_state)) { - FLOAT32 new_merit = prioritize_state(chunks_record, the_search); - if (new_merit < worst_priority) { - if (wordrec_debug_level > 1) - log_state("Pushing segstate", the_search->num_joints, - the_search->this_state, new_merit); - push_queue(the_search->open_states, the_search->this_state, - worst_priority, new_merit, wordrec_debug_level > 1); - } else { - if (wordrec_debug_level > 1) - log_state("Ignore weak segstate", the_search->num_joints, - the_search->this_state, new_merit); - } - } - mask >>= 1; - } - - if (the_search->num_joints > 32) { - mask = 1 << 31; - } - else { - mask = 1 << (the_search->num_joints - 1); - } - - the_search->this_state->part1 = old_state.part1; - while (x--) { - the_search->this_state->part2 = mask ^ old_state.part2; - if (!hash_lookup (the_search->closed_states, the_search->this_state)) { - FLOAT32 new_merit = prioritize_state(chunks_record, the_search); - if (new_merit < worst_priority) { - if (wordrec_debug_level > 1) - log_state("Pushing segstate", the_search->num_joints, - the_search->this_state, new_merit); - push_queue(the_search->open_states, the_search->this_state, - worst_priority, new_merit, wordrec_debug_level > 1); - } else { - if (wordrec_debug_level > 1) - log_state("Ignoring weak segstate", the_search->num_joints, - the_search->this_state, new_merit); - } - } - mask >>= 1; - } -} - -/** - * @name new_search - * - * Create and initialize a new search record. - */ -SEARCH_RECORD *Wordrec::new_search(CHUNKS_RECORD *chunks_record, - int num_joints, - BLOB_CHOICE_LIST_VECTOR *best_char_choices, - WERD_CHOICE *best_choice, - WERD_CHOICE *raw_choice, - STATE *state) { - SEARCH_RECORD *this_search; - - this_search = (SEARCH_RECORD *) memalloc (sizeof (SEARCH_RECORD)); - - this_search->open_states = MakeHeap (wordrec_num_seg_states * 20); - this_search->closed_states = new_hash_table(); - - if (state) - this_search->this_state = new_state (state); - else - cprintf ("error: bad initial state in new_search\n"); - - this_search->first_state = new_state (this_search->this_state); - this_search->best_state = new_state (this_search->this_state); - - this_search->best_choice = best_choice; - this_search->raw_choice = raw_choice; - this_search->best_char_choices = best_char_choices; - - this_search->num_joints = num_joints; - this_search->num_states = 0; - this_search->before_best = 0; - this_search->segcost_bias = 0; - - return (this_search); -} - -/** - * @name pop_queue - * - * Get this state from the priority queue. It should be the state that - * has the greatest urgency to be evaluated. - */ -STATE *Wordrec::pop_queue(HEAP *queue) { - HEAPENTRY entry; - - if (GetTopOfHeap (queue, &entry) == TESS_HEAP_OK) { -#ifndef GRAPHICS_DISABLED - if (wordrec_display_segmentations) { - cprintf ("eval state: %8.3f ", entry.Key); - print_state ("", (STATE *) entry.Data, num_joints); - } -#endif - return ((STATE *) entry.Data); - } - else { - return (NULL); - } -} - -/** - * @name push_queue - * - * Add this state into the priority queue. - */ -void Wordrec::push_queue(HEAP *queue, STATE *state, FLOAT32 worst_priority, - FLOAT32 priority, bool debug) { - HEAPENTRY entry; - - if (priority < worst_priority) { - if (SizeOfHeap (queue) >= MaxSizeOfHeap(queue)) { - if (debug) tprintf("Heap is Full\n"); - return; - } - entry.Data = (char *) new_state (state); - num_pushed++; - entry.Key = priority; - HeapStore(queue, &entry); - } -} - -/** - * @name replace_char_widths - * - * Replace the value of the char_width field in the chunks_record with - * the updated width measurements from the last_segmentation. - */ -void Wordrec::replace_char_widths(CHUNKS_RECORD *chunks_record, - SEARCH_STATE state) { - WIDTH_RECORD *width_record; - int num_blobs; - int i; - - free_widths (chunks_record->char_widths); - - num_blobs = state[0] + 1; - width_record = (WIDTH_RECORD *) memalloc (sizeof (int) * num_blobs * 2); - width_record->num_chars = num_blobs; - - for (i = 0; i < num_blobs; i++) { - - width_record->widths[2 * i] = last_segmentation[i].width; - - if (i + 1 < num_blobs) - width_record->widths[2 * i + 1] = last_segmentation[i].gap; - } - chunks_record->char_widths = width_record; -} - -// Creates a fake blob choice from the combination of the given fragments. -// unichar is the class to be made from the combination, -// expanded_fragment_lengths[choice_index] is the number of fragments to use. -// old_choices[choice_index] has the classifier output for each fragment. -// choice index initially indexes the last fragment and should be decremented -// expanded_fragment_lengths[choice_index] times to get the earlier fragments. -// Guarantees to return something non-null, or abort! -BLOB_CHOICE* Wordrec::rebuild_fragments( - const char* unichar, - const char* expanded_fragment_lengths, - int choice_index, - BLOB_CHOICE_LIST_VECTOR *old_choices) { - float rating = 0.0f; - float certainty = 0.0f; - inT16 min_xheight = -MAX_INT16; - inT16 max_xheight = MAX_INT16; - for (int fragment_pieces = expanded_fragment_lengths[choice_index] - 1; - fragment_pieces >= 0; --fragment_pieces, --choice_index) { - // Get a pointer to the classifier results from the old_choices. - BLOB_CHOICE_LIST *current_choices = old_choices->get(choice_index); - // Populate fragment with updated values and look for the - // fragment with the same values in current_choices. - // Update rating and certainty of the character being composed. - CHAR_FRAGMENT fragment; - fragment.set_all(unichar, fragment_pieces, - expanded_fragment_lengths[choice_index], false); - BLOB_CHOICE_IT choice_it(current_choices); - for (choice_it.mark_cycle_pt(); !choice_it.cycled_list(); - choice_it.forward()) { - BLOB_CHOICE* choice = choice_it.data(); - const CHAR_FRAGMENT *current_fragment = - getDict().getUnicharset().get_fragment(choice->unichar_id()); - if (current_fragment && fragment.equals(current_fragment)) { - rating += choice->rating(); - if (choice->certainty() < certainty) { - certainty = choice->certainty(); - } - IntersectRange(choice->min_xheight(), choice->max_xheight(), - &min_xheight, &max_xheight); - break; - } - } - if (choice_it.cycled_list()) { - print_ratings_list("Failure", current_choices, unicharset); - tprintf("Failed to find fragment %s at index=%d\n", - fragment.to_string().string(), choice_index); - } - ASSERT_HOST(!choice_it.cycled_list()); // Be sure we found the fragment. - } - return new BLOB_CHOICE(getDict().getUnicharset().unichar_to_id(unichar), - rating, certainty, -1, -1, 0, - min_xheight, max_xheight, false); -} - -// Creates a joined copy of the blobs between x and y (inclusive) and -// inserts as the first blob at word->rebuild_word->blobs. -// Returns a deep copy of the classifier results for the blob. -BLOB_CHOICE_LIST *Wordrec::join_blobs_and_classify( - WERD_RES* word, int x, int y, int choice_index, MATRIX *ratings, - BLOB_CHOICE_LIST_VECTOR *old_choices) { - // Join parts to make the blob if needed. - if (x != y) - join_pieces(word->chopped_word->blobs, word->seam_array, x, y); - TBLOB *blob = word->chopped_word->blobs; - for (int i = 0; i < x; i++) { - blob = blob->next; - } - // Deep copy this blob into the output word. - TBLOB* copy_blob = new TBLOB(*blob); - copy_blob->next = word->rebuild_word->blobs; - word->rebuild_word->blobs = copy_blob; - - BLOB_CHOICE_LIST *choices = NULL; - // First check to see if we can look up the classificaiton - // in old_choices (if there is no need to merge blobs). - if (choice_index >= 0 && old_choices != NULL) { - choices = old_choices->get(choice_index); - old_choices->set(NULL, choice_index); - } - // The ratings matrix filled in by the associator will contain the next most - // up-to-date classification info. Thus we look up the classification there - // next, and only call classify_blob() if the classification is not found. - if (choices == NULL && ratings != NULL) { - choices = ratings->get(x, y); - if (choices != NOT_CLASSIFIED) { - ratings->put(x, y, NULL); - } - } - // Get the choices for the blob by classification if necessary. - if (choices == NULL) { - choices = classify_blob(blob, word->denorm, "rebuild", Orange, - word->blamer_bundle); - } - // Undo join_pieces to restore the chopped word to its fully chopped state. - if (x != y) - break_pieces(blob, word->seam_array, x, y); - return choices; -} - -} // namespace tesseract diff --git a/wordrec/bestfirst.h b/wordrec/bestfirst.h deleted file mode 100644 index 7750958d86..0000000000 --- a/wordrec/bestfirst.h +++ /dev/null @@ -1,70 +0,0 @@ -/* -*-C-*- - ******************************************************************************** - * - * File: bestfirst.h (Formerly bestfirst.h) - * Description: Best first search functions - * Author: Mark Seaman, OCR Technology - * Created: Mon May 14 11:23:29 1990 - * Modified: Mon Apr 29 14:21:57 1991 (Mark Seaman) marks@hpgrlt - * Language: C - * Package: N/A - * Status: Experimental (Do Not Distribute) - * - * (c) Copyright 1990, Hewlett-Packard Company. - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** http://www.apache.org/licenses/LICENSE-2.0 - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - * - *******************************************************************************/ - -#ifndef BESTFIRST_H -#define BESTFIRST_H - -/*---------------------------------------------------------------------- - I n c l u d e s -----------------------------------------------------------------------*/ - -#include "associate.h" -#include "blobs.h" -#include "closed.h" -#include "oldheap.h" -#include "ratngs.h" -#include "seam.h" -#include "states.h" -#include "stopper.h" - -/*---------------------------------------------------------------------- - T y p e s -----------------------------------------------------------------------*/ -struct SEARCH_RECORD -{ - HEAP *open_states; - HASH_TABLE closed_states; - STATE *this_state; - STATE *first_state; - STATE *best_state; - int num_joints; - long num_states; - long before_best; - float segcost_bias; - WERD_CHOICE *best_choice; - WERD_CHOICE *raw_choice; - BLOB_CHOICE_LIST_VECTOR *best_char_choices; -}; - - -/*---------------------------------------------------------------------- - F u n c t i o n s -----------------------------------------------------------------------*/ -int chunks_width(WIDTH_RECORD *width_record, int start_chunk, int last_chunk); -int chunks_gap(WIDTH_RECORD *width_record, int last_chunk); -STATE *pop_queue(HEAP *queue); -void replace_char_widths(CHUNKS_RECORD *chunks_record, SEARCH_STATE state); - -#endif diff --git a/wordrec/chop.cpp b/wordrec/chop.cpp index 9972db177d..9ae61bb932 100644 --- a/wordrec/chop.cpp +++ b/wordrec/chop.cpp @@ -62,13 +62,10 @@ PRIORITY Wordrec::point_priority(EDGEPT *point) { * * Add an edge point to a POINT_GROUP containg a list of other points. */ -void Wordrec::add_point_to_list(POINT_GROUP point_list, EDGEPT *point) { - HEAPENTRY data; - - if (SizeOfHeap (point_list) < MAX_NUM_POINTS - 2) { - data.Data = (char *) point; - data.Key = point_priority (point); - HeapStore(point_list, &data); +void Wordrec::add_point_to_list(PointHeap* point_heap, EDGEPT *point) { + if (point_heap->size() < MAX_NUM_POINTS - 2) { + PointPair pair(point_priority(point), point); + point_heap->Push(&pair); } #ifndef GRAPHICS_DISABLED @@ -217,7 +214,7 @@ EDGEPT *Wordrec::pick_close_point(EDGEPT *critical_point, * each of these points assign a priority. Sort these points using a * heap structure so that they can be visited in order. */ -void Wordrec::prioritize_points(TESSLINE *outline, POINT_GROUP points) { +void Wordrec::prioritize_points(TESSLINE *outline, PointHeap* points) { EDGEPT *this_point; EDGEPT *local_min = NULL; EDGEPT *local_max = NULL; @@ -276,7 +273,7 @@ void Wordrec::prioritize_points(TESSLINE *outline, POINT_GROUP points) { * Return the new value for the local minimum. If a point is saved then * the local minimum is reset to NULL. */ -void Wordrec::new_min_point(EDGEPT *local_min, POINT_GROUP points) { +void Wordrec::new_min_point(EDGEPT *local_min, PointHeap* points) { inT16 dir; dir = direction (local_min); @@ -300,7 +297,7 @@ void Wordrec::new_min_point(EDGEPT *local_min, POINT_GROUP points) { * Return the new value for the local minimum. If a point is saved then * the local minimum is reset to NULL. */ -void Wordrec::new_max_point(EDGEPT *local_max, POINT_GROUP points) { +void Wordrec::new_max_point(EDGEPT *local_max, PointHeap* points) { inT16 dir; dir = direction (local_max); @@ -344,11 +341,12 @@ void Wordrec::vertical_projection_point(EDGEPT *split_point, EDGEPT *target_poin p = target_point; /* Look at each edge point */ do { - if ((((p->pos.x <= x) && (x <= p->next->pos.x)) || - ((p->next->pos.x <= x) && (x <= p->pos.x))) && - !same_point (split_point->pos, p->pos) && - !same_point (split_point->pos, p->next->pos) - && (*best_point == NULL || !same_point ((*best_point)->pos, p->pos))) { + if (((p->pos.x <= x && x <= p->next->pos.x) || + (p->next->pos.x <= x && x <= p->pos.x)) && + !same_point(split_point->pos, p->pos) && + !same_point(split_point->pos, p->next->pos) && + !p->IsChopPt() && + (*best_point == NULL || !same_point((*best_point)->pos, p->pos))) { if (near_point(split_point, p, p->next, &this_edgept)) { new_point_it.add_before_then_move(this_edgept); diff --git a/wordrec/chop.h b/wordrec/chop.h index 7c159acf89..bafc99bae9 100644 --- a/wordrec/chop.h +++ b/wordrec/chop.h @@ -29,14 +29,16 @@ /*---------------------------------------------------------------------- I n c l u d e s ----------------------------------------------------------------------*/ -#include "oldheap.h" +#include "genericheap.h" +#include "kdpair.h" #include "seam.h" /*---------------------------------------------------------------------- T y p e s ---------------------------------------------------------------------*/ #define MAX_NUM_POINTS 50 -typedef HEAP *POINT_GROUP; -typedef HEAP *SPLIT_GROUP; +// The PointPair elements do NOT own the EDGEPTs. +typedef tesseract::KDPairInc PointPair; +typedef tesseract::GenericHeap PointHeap; #endif diff --git a/wordrec/chopper.cpp b/wordrec/chopper.cpp index 4b38959b7d..f7603f6a02 100644 --- a/wordrec/chopper.cpp +++ b/wordrec/chopper.cpp @@ -33,6 +33,7 @@ #include "assert.h" #include "associate.h" +#include "blobs.h" #include "callcpp.h" #include "const.h" #include "findseam.h" @@ -41,12 +42,10 @@ #include "makechop.h" #include "render.h" #include "pageres.h" -#include "permute.h" #include "seam.h" #include "stopper.h" #include "structures.h" #include "unicharset.h" -#include "wordclass.h" #include "wordrec.h" // Include automatically generated configuration file if running autoconf. @@ -54,6 +53,10 @@ #include "config_auto.h" #endif +// Even though the limit on the number of chunks may now be removed, keep +// the same limit for repeatable behavior, and it may be a speed advantage. +static const int kMaxNumChunks = 64; + /*---------------------------------------------------------------------- F u n c t i o n s ----------------------------------------------------------------------*/ @@ -95,7 +98,6 @@ void preserve_outline_tree(TESSLINE *srcline) { EDGEPT *restore_outline(EDGEPT *start) { EDGEPT *srcpt; EDGEPT *real_start; - EDGEPT *deadpt; if (start == NULL) return NULL; @@ -108,17 +110,10 @@ EDGEPT *restore_outline(EDGEPT *start) { while (srcpt != start); real_start = srcpt; do { - if (srcpt->flags[1] == 0) { - deadpt = srcpt; - srcpt = srcpt->next; - srcpt->prev = deadpt->prev; - deadpt->prev->next = srcpt; - deadpt->prev->vec.x = srcpt->pos.x - deadpt->prev->pos.x; - deadpt->prev->vec.y = srcpt->pos.y - deadpt->prev->pos.y; - delete deadpt; + srcpt = srcpt->next; + if (srcpt->prev->flags[1] == 0) { + remove_edgept(srcpt->prev); } - else - srcpt = srcpt->next; } while (srcpt != real_start); return real_start; @@ -135,6 +130,40 @@ void restore_outline_tree(TESSLINE *srcline) { } } +// Helper runs all the checks on a seam to make sure it is valid. +// Returns the seam if OK, otherwise deletes the seam and returns NULL. +static SEAM* CheckSeam(int debug_level, inT32 blob_number, TWERD* word, + TBLOB* blob, TBLOB* other_blob, + const GenericVector& seams, SEAM* seam) { + if (seam == NULL || + blob->outlines == NULL || + other_blob->outlines == NULL || + total_containment(blob, other_blob) || + check_blob(other_blob) || + !(check_seam_order(blob, seam) && + check_seam_order(other_blob, seam)) || + any_shared_split_points(seams, seam) || + !test_insert_seam(seams, word, blob_number)) { + word->blobs.remove(blob_number + 1); + if (seam) { + undo_seam(blob, other_blob, seam); + delete seam; + seam = NULL; +#ifndef GRAPHICS_DISABLED + if (debug_level) { + if (debug_level >2) + display_blob(blob, Red); + tprintf("\n** seam being removed ** \n"); + } +#endif + } else { + delete other_blob; + } + return NULL; + } + return seam; +} + /** * @name attempt_blob_chop @@ -144,101 +173,69 @@ void restore_outline_tree(TESSLINE *srcline) { */ namespace tesseract { SEAM *Wordrec::attempt_blob_chop(TWERD *word, TBLOB *blob, inT32 blob_number, - bool italic_blob, SEAMS seam_list) { - TBLOB *next_blob = blob->next; - TBLOB *other_blob; - SEAM *seam; - + bool italic_blob, + const GenericVector& seams) { if (repair_unchopped_blobs) preserve_outline_tree (blob->outlines); - other_blob = new TBLOB; /* Make new blob */ - other_blob->next = blob->next; - other_blob->outlines = NULL; - blob->next = other_blob; + TBLOB *other_blob = TBLOB::ShallowCopy(*blob); /* Make new blob */ + // Insert it into the word. + word->blobs.insert(other_blob, blob_number + 1); - seam = NULL; + SEAM *seam = NULL; if (prioritize_division) { TPOINT location; if (divisible_blob(blob, italic_blob, &location)) { - seam = new_seam(0.0f, location, NULL, NULL, NULL); + seam = new SEAM(0.0f, location, NULL, NULL, NULL); } } if (seam == NULL) seam = pick_good_seam(blob); - if (seam == NULL && word->latin_script) { - // If the blob can simply be divided into outlines, then do that. - TPOINT location; - if (divisible_blob(blob, italic_blob, &location)) { - seam = new_seam(0.0f, location, NULL, NULL, NULL); - } - } if (chop_debug) { - if (seam != NULL) { - print_seam ("Good seam picked=", seam); - } + if (seam != NULL) + print_seam("Good seam picked=", seam); else - cprintf ("\n** no seam picked *** \n"); + tprintf("\n** no seam picked *** \n"); } if (seam) { apply_seam(blob, other_blob, italic_blob, seam); } - if ((seam == NULL) || - (blob->outlines == NULL) || - (other_blob->outlines == NULL) || - total_containment (blob, other_blob) || - check_blob (other_blob) || - !(check_seam_order (blob, seam) && - check_seam_order (other_blob, seam)) || - any_shared_split_points (seam_list, seam) || - !test_insert_seam(seam_list, blob_number, blob, word->blobs)) { - - blob->next = next_blob; - if (seam) { - undo_seam(blob, other_blob, seam); - delete_seam(seam); -#ifndef GRAPHICS_DISABLED - if (chop_debug) { - if (chop_debug >2) - display_blob(blob, Red); - cprintf ("\n** seam being removed ** \n"); + seam = CheckSeam(chop_debug, blob_number, word, blob, other_blob, + seams, seam); + if (seam == NULL) { + if (repair_unchopped_blobs) + restore_outline_tree(blob->outlines); + if (word->latin_script) { + // If the blob can simply be divided into outlines, then do that. + TPOINT location; + if (divisible_blob(blob, italic_blob, &location)) { + other_blob = TBLOB::ShallowCopy(*blob); /* Make new blob */ + word->blobs.insert(other_blob, blob_number + 1); + seam = new SEAM(0.0f, location, NULL, NULL, NULL); + apply_seam(blob, other_blob, italic_blob, seam); + seam = CheckSeam(chop_debug, blob_number, word, blob, other_blob, + seams, seam); } -#endif - } else { - delete other_blob; } - - if (repair_unchopped_blobs) - restore_outline_tree (blob->outlines); - return (NULL); } - return (seam); + return seam; } SEAM *Wordrec::chop_numbered_blob(TWERD *word, inT32 blob_number, - bool italic_blob, SEAMS seam_list) { - TBLOB *blob; - inT16 x; - - blob = word->blobs; - for (x = 0; x < blob_number; x++) - blob = blob->next; - - return attempt_blob_chop(word, blob, blob_number, - italic_blob, seam_list); + bool italic_blob, + const GenericVector& seams) { + return attempt_blob_chop(word, word->blobs[blob_number], blob_number, + italic_blob, seams); } SEAM *Wordrec::chop_overlapping_blob(const GenericVector& boxes, - WERD_RES *word_res, inT32 *blob_number, - bool italic_blob, SEAMS seam_list) { + bool italic_blob, WERD_RES *word_res, + int *blob_number) { TWERD *word = word_res->chopped_word; - TBLOB *blob; - - *blob_number = 0; - blob = word->blobs; - while (blob != NULL) { + for (*blob_number = 0; *blob_number < word->NumBlobs(); ++*blob_number) { + TBLOB *blob = word->blobs[*blob_number]; TPOINT topleft, botright; topleft.x = blob->bounding_box().left(); topleft.y = blob->bounding_box().top(); @@ -246,8 +243,8 @@ SEAM *Wordrec::chop_overlapping_blob(const GenericVector& boxes, botright.y = blob->bounding_box().bottom(); TPOINT original_topleft, original_botright; - word_res->denorm.DenormTransform(topleft, &original_topleft); - word_res->denorm.DenormTransform(botright, &original_botright); + word_res->denorm.DenormTransform(NULL, topleft, &original_topleft); + word_res->denorm.DenormTransform(NULL, botright, &original_botright); TBOX original_box = TBOX(original_topleft.x, original_botright.y, original_botright.x, original_topleft.y); @@ -265,13 +262,10 @@ SEAM *Wordrec::chop_overlapping_blob(const GenericVector& boxes, if (divisible_blob(blob, italic_blob, &location) || (!almost_equal_box && num_overlap > 1)) { SEAM *seam = attempt_blob_chop(word, blob, *blob_number, - italic_blob, seam_list); + italic_blob, word_res->seam_array); if (seam != NULL) return seam; } - - *blob_number = *blob_number + 1; - blob = blob->next; } *blob_number = -1; @@ -286,13 +280,13 @@ SEAM *Wordrec::chop_overlapping_blob(const GenericVector& boxes, * * Return true if any of the splits share a point with this one. */ -int any_shared_split_points(SEAMS seam_list, SEAM *seam) { +int any_shared_split_points(const GenericVector& seams, SEAM *seam) { int length; int index; - length = array_count (seam_list); + length = seams.size(); for (index = 0; index < length; index++) - if (shared_split_points ((SEAM *) array_value (seam_list, index), seam)) + if (shared_split_points(seams[index], seam)) return TRUE; return FALSE; } @@ -326,111 +320,52 @@ namespace tesseract { /** * @name improve_one_blob * - * Start with the current word of blobs and its classification. Find - * the worst blobs and try to divide it up to improve the ratings. + * Finds the best place to chop, based on the worst blob, fixpt, or next to + * a fragment, according to the input. Returns the SEAM corresponding to the + * chop point, if any is found, and the index in the ratings_matrix of the + * chopped blob. Note that blob_choices is just a copy of the pointers in the + * leading diagonal of the ratings MATRIX. + * Although the blob is chopped, the returned SEAM is yet to be inserted into + * word->seam_array and the resulting blobs are unclassified, so this function + * can be used by ApplyBox as well as during recognition. */ -bool Wordrec::improve_one_blob(WERD_RES *word_res, - BLOB_CHOICE_LIST_VECTOR *char_choices, - inT32 *blob_number, - SEAMS *seam_list, - DANGERR *fixpt, - bool split_next_to_fragment, - BlamerBundle *blamer_bundle) { - TWERD* word = word_res->chopped_word; - TBLOB *blob; - inT16 x = 0; +SEAM* Wordrec::improve_one_blob(const GenericVector& blob_choices, + DANGERR *fixpt, + bool split_next_to_fragment, + bool italic_blob, + WERD_RES* word, + int* blob_number) { float rating_ceiling = MAX_FLOAT32; - BLOB_CHOICE_LIST *answer; - BLOB_CHOICE_IT answer_it; - SEAM *seam; - + SEAM *seam = NULL; do { *blob_number = select_blob_to_split_from_fixpt(fixpt); + if (chop_debug) tprintf("blob_number from fixpt = %d\n", *blob_number); bool split_point_from_dict = (*blob_number != -1); if (split_point_from_dict) { fixpt->clear(); } else { - *blob_number = select_blob_to_split(*char_choices, rating_ceiling, + *blob_number = select_blob_to_split(blob_choices, rating_ceiling, split_next_to_fragment); } - if (chop_debug) - cprintf("blob_number = %d\n", *blob_number); + if (chop_debug) tprintf("blob_number = %d\n", *blob_number); if (*blob_number == -1) - return false; + return NULL; // TODO(rays) it may eventually help to allow italic_blob to be true, - seam = chop_numbered_blob(word, *blob_number, false, *seam_list); + seam = chop_numbered_blob(word->chopped_word, *blob_number, italic_blob, + word->seam_array); if (seam != NULL) - break; - /* Must split null blobs */ - answer = char_choices->get(*blob_number); - if (answer == NULL) - return false; - answer_it.set_to_list(answer); + return seam; // Success! + if (blob_choices[*blob_number] == NULL) + return NULL; if (!split_point_from_dict) { // We chopped the worst rated blob, try something else next time. - rating_ceiling = answer_it.data()->rating(); + rating_ceiling = blob_choices[*blob_number]->rating(); } } while (true); - /* Split OK */ - for (blob = word->blobs; x < *blob_number; x++) { - blob = blob->next; - } - - *seam_list = - insert_seam (*seam_list, *blob_number, seam, blob, word->blobs); - - delete char_choices->get(*blob_number); - - answer = classify_blob(blob, word_res->denorm, "improve 1:", Red, - blamer_bundle); - char_choices->insert(answer, *blob_number); - - answer = classify_blob(blob->next, word_res->denorm, "improve 2:", Yellow, - blamer_bundle); - char_choices->set(answer, *blob_number + 1); - - return true; + return seam; } -/** - * @name modify_blob_choice - * - * Takes a blob and its chop index, converts that chop index to a - * unichar_id, and stores the chop index in place of the blob's - * original unichar_id. - */ -void Wordrec::modify_blob_choice(BLOB_CHOICE_LIST *answer, - int chop_index) { - char chop_index_string[2]; - if (chop_index <= 9) { - snprintf(chop_index_string, sizeof(chop_index_string), "%d", chop_index); - } else { - chop_index_string[0] = static_cast('A' - 10 + chop_index); - chop_index_string[1] = '\0'; - } - UNICHAR_ID unichar_id = unicharset.unichar_to_id(chop_index_string); - if (unichar_id == INVALID_UNICHAR_ID) { - // If the word is very long, we might exhaust the possibilities. - unichar_id = 1; - } - BLOB_CHOICE_IT answer_it(answer); - BLOB_CHOICE *modified_blob = - new BLOB_CHOICE(unichar_id, - answer_it.data()->rating(), - answer_it.data()->certainty(), - answer_it.data()->fontinfo_id(), - answer_it.data()->fontinfo_id2(), - answer_it.data()->script_id(), - answer_it.data()->min_xheight(), - answer_it.data()->max_xheight(), - answer_it.data()->adapted()); - answer->clear(); - answer_it.set_to_list(answer); - answer_it.add_after_then_move(modified_blob); -} - - /** * @name chop_one_blob * @@ -438,93 +373,16 @@ void Wordrec::modify_blob_choice(BLOB_CHOICE_LIST *answer, * the worst blobs and try to divide it up to improve the ratings. * Used for testing chopper. */ -bool Wordrec::chop_one_blob(TWERD *word, - BLOB_CHOICE_LIST_VECTOR *char_choices, - inT32 *blob_number, - SEAMS *seam_list, - int *right_chop_index) { - TBLOB *blob; - inT16 x = 0; - float rating_ceiling = MAX_FLOAT32; - BLOB_CHOICE_LIST *answer; - BLOB_CHOICE_IT answer_it; - SEAM *seam; - UNICHAR_ID unichar_id = 0; - int left_chop_index = 0; - - do { - *blob_number = select_blob_to_split(*char_choices, rating_ceiling, false); - if (chop_debug) - cprintf("blob_number = %d\n", *blob_number); - if (*blob_number == -1) - return false; - seam = chop_numbered_blob(word, *blob_number, true, *seam_list); - if (seam != NULL) - break; - /* Must split null blobs */ - answer = char_choices->get(*blob_number); - if (answer == NULL) - return false; - answer_it.set_to_list(answer); - rating_ceiling = answer_it.data()->rating(); // try a different blob - } while (true); - /* Split OK */ - for (blob = word->blobs; x < *blob_number; x++) { - blob = blob->next; - } - if (chop_debug) { - tprintf("Chop made blob1:"); - blob->bounding_box().print(); - tprintf("and blob2:"); - blob->next->bounding_box().print(); - } - *seam_list = insert_seam(*seam_list, *blob_number, seam, blob, word->blobs); - - answer = char_choices->get(*blob_number); - answer_it.set_to_list(answer); - unichar_id = answer_it.data()->unichar_id(); - float rating = answer_it.data()->rating() / exp(1.0); - left_chop_index = atoi(unicharset.id_to_unichar(unichar_id)); - - delete char_choices->get(*blob_number); - // combine confidence w/ serial # - answer = fake_classify_blob(0, rating, -rating); - modify_blob_choice(answer, left_chop_index); - char_choices->insert(answer, *blob_number); - - answer = fake_classify_blob(0, rating - 0.125f, -rating); - modify_blob_choice(answer, ++*right_chop_index); - char_choices->set(answer, *blob_number + 1); - return true; -} - - -bool Wordrec::chop_one_blob2(const GenericVector& boxes, - WERD_RES *word_res, - SEAMS *seam_list) { - inT32 blob_number; - inT16 x = 0; - TBLOB *blob; - SEAM *seam; - - seam = chop_overlapping_blob(boxes, word_res, &blob_number, - true, *seam_list); - if (seam == NULL) - return false; - - /* Split OK */ - for (blob = word_res->chopped_word->blobs; x < blob_number; x++) { - blob = blob->next; - } - if (chop_debug) { - tprintf("Chop made blob1:"); - blob->bounding_box().print(); - tprintf("and blob2:"); - blob->next->bounding_box().print(); +SEAM* Wordrec::chop_one_blob(const GenericVector& boxes, + const GenericVector& blob_choices, + WERD_RES* word_res, + int* blob_number) { + if (prioritize_division) { + return chop_overlapping_blob(boxes, true, word_res, blob_number); + } else { + return improve_one_blob(blob_choices, NULL, false, true, word_res, + blob_number); } - *seam_list = insert_seam(*seam_list, blob_number, seam, blob, - word_res->chopped_word->blobs); - return true; } } // namespace tesseract @@ -572,222 +430,136 @@ inT16 check_seam_order(TBLOB *blob, SEAM *seam) { } namespace tesseract { + /** * @name chop_word_main * * Classify the blobs in this word and permute the results. Find the * worst blob in the word and chop it up. Continue this process until * a good answer has been found or all the blobs have been chopped up - * enough. Return the word level ratings. + * enough. The results are returned in the WERD_RES. */ -BLOB_CHOICE_LIST_VECTOR *Wordrec::chop_word_main(WERD_RES *word) { - TBLOB *blob; - int index; - int did_chopping; - STATE state; - BLOB_CHOICE_LIST *match_result; - MATRIX *ratings = NULL; - DANGERR fixpt; /*dangerous ambig */ - inT32 bit_count; //no of bits - - BLOB_CHOICE_LIST_VECTOR *char_choices = new BLOB_CHOICE_LIST_VECTOR(); - BLOB_CHOICE_LIST_VECTOR *best_char_choices = new BLOB_CHOICE_LIST_VECTOR(); - - did_chopping = 0; - for (blob = word->chopped_word->blobs, index = 0; - blob != NULL; blob = blob->next, index++) { - match_result = classify_blob(blob, word->denorm, "chop_word:", Green, - word->blamer_bundle); - if (match_result == NULL) - cprintf("Null classifier output!\n"); - *char_choices += match_result; - } - bit_count = index - 1; - set_n_ones(&state, char_choices->length() - 1); - bool acceptable = false; - bool replaced = false; - bool best_choice_updated = - getDict().permute_characters(*char_choices, word->best_choice, - word->raw_choice); - if (best_choice_updated && - getDict().AcceptableChoice(char_choices, word->best_choice, &fixpt, - CHOPPER_CALLER, &replaced)) { - acceptable = true; - } - if (replaced) - update_blob_classifications(word->chopped_word, *char_choices); - CopyCharChoices(*char_choices, best_char_choices); - if (!acceptable) { // do more work to find a better choice - did_chopping = 1; - - bool best_choice_acceptable = false; - if (chop_enable) - improve_by_chopping(word, - char_choices, - &state, - best_char_choices, - &fixpt, - &best_choice_acceptable); - if (chop_debug) - print_seams ("Final seam list:", word->seam_array); - - if (word->blamer_bundle != NULL && - !ChoiceIsCorrect(*word->uch_set, word->best_choice, - word->blamer_bundle->truth_text)) { - set_chopper_blame(word); - } - - // The force_word_assoc is almost redundant to enable_assoc. However, - // it is not conditioned on the dict behavior. For CJK, we need to force - // the associator to be invoked. When we figure out the exact behavior - // of dict on CJK, we can remove the flag if it turns out to be redundant. - if ((wordrec_enable_assoc && !best_choice_acceptable) || force_word_assoc) { - ratings = word_associator(false, word, &state, best_char_choices, - &fixpt, &state); - } - } - best_char_choices = rebuild_current_state(word, &state, best_char_choices, - ratings); - - // If after running only the chopper best_choice is incorrect and no blame - // has been yet set, blame the classifier if best_choice is classifier's - // top choice and is a dictionary word (i.e. language model could not have - // helped). Otherwise blame the tradeoff between the classifier and - // the old language model (permuters). - if (word->blamer_bundle != NULL && - word->blamer_bundle->incorrect_result_reason == IRR_CORRECT && - ratings == NULL && // only the chopper was run - !ChoiceIsCorrect(*word->uch_set, word->best_choice, - word->blamer_bundle->truth_text)) { - if (word->best_choice != NULL && - Dict::valid_word_permuter(word->best_choice->permuter(), false)) { - // Find out whether best choice is a top choice. - word->blamer_bundle->best_choice_is_dict_and_top_choice = true; - for (int i = 0; i < word->best_choice->length(); ++i) { - BLOB_CHOICE_IT blob_choice_it(best_char_choices->get(i)); - ASSERT_HOST(!blob_choice_it.empty()); - BLOB_CHOICE *first_choice = NULL; - for (blob_choice_it.mark_cycle_pt(); !blob_choice_it.cycled_list(); - blob_choice_it.forward()) { // find first non-fragment choice - if (!(getDict().getUnicharset().get_fragment( - blob_choice_it.data()->unichar_id()))) { - first_choice = blob_choice_it.data(); - break; - } - } - ASSERT_HOST(first_choice != NULL); - if (first_choice->unichar_id() != word->best_choice->unichar_id(i)) { - word->blamer_bundle->best_choice_is_dict_and_top_choice = false; - break; - } - } - } - STRING debug; - if (word->blamer_bundle->best_choice_is_dict_and_top_choice) { - debug = "Best choice is: incorrect, top choice, dictionary word"; - debug += " with permuter "; - debug += word->best_choice->permuter_name(); - } else { - debug = "Classifier/Old LM tradeoff is to blame"; - } - word->blamer_bundle->SetBlame( - word->blamer_bundle->best_choice_is_dict_and_top_choice ? - IRR_CLASSIFIER : IRR_CLASS_OLD_LM_TRADEOFF, - debug, word->best_choice, wordrec_debug_blamer); +void Wordrec::chop_word_main(WERD_RES *word) { + // Initial clean up. + word->ClearRatings(); + int num_blobs = word->chopped_word->NumBlobs(); + word->ratings = new MATRIX(num_blobs, wordrec_max_join_chunks); + // Run initial classification. + for (int b = 0; b < num_blobs; ++b) { + BLOB_CHOICE_LIST* choices = classify_piece(word->seam_array, b, b, + "Initial:", word->chopped_word, + word->blamer_bundle); + word->ratings->put(b, b, choices); + } + + // Run Segmentation Search. + BestChoiceBundle best_choice_bundle(word->ratings->dimension()); + SegSearch(word, &best_choice_bundle, word->blamer_bundle); + + if (word->best_choice == NULL) { + // SegSearch found no valid paths, so just use the leading diagonal. + word->FakeWordFromRatings(); + } + word->RebuildBestState(); + // If we finished without a hyphen at the end of the word, let the next word + // be found in the dictionary. + if (word->word->flag(W_EOL) && + !getDict().has_hyphen_end(*word->best_choice)) { + getDict().reset_hyphen_vars(true); } if (word->blamer_bundle != NULL && this->fill_lattice_ != NULL) { - if (ratings == NULL) { - ratings = word_associator(true, word, NULL, NULL, NULL, NULL); - } - CallFillLattice(*ratings, getDict().getBestChoices(), + CallFillLattice(*word->ratings, word->best_choices, *word->uch_set, word->blamer_bundle); } - if (ratings != NULL) { - if (wordrec_debug_level > 0) { - tprintf("Final Ratings Matrix:\n"); - ratings->print(getDict().getUnicharset()); - } - ratings->delete_matrix_pointers(); - delete ratings; + if (wordrec_debug_level > 0) { + tprintf("Final Ratings Matrix:\n"); + word->ratings->print(getDict().getUnicharset()); } - getDict().FilterWordChoices(); - // TODO(antonova, eger): check that FilterWordChoices() does not filter - // out anything useful for word bigram or phrase search. - // TODO(antonova, eger): when implementing word bigram and phrase search - // we will need to think carefully about how to replace a word with its - // alternative choice. - // In particular it might be required to save the segmentation state - // associated with the word, so that best_char_choices could be updated - // by rebuild_current_state() correctly. - if (save_alt_choices) SaveAltChoices(getDict().getBestChoices(), word); - char_choices->delete_data_pointers(); - delete char_choices; - - return best_char_choices; + word->FilterWordChoices(getDict().stopper_debug_level); } - - /** * @name improve_by_chopping * - * Start with the current word of blobs and its classification. Find - * the worst blobs and try to divide them up to improve the ratings. - * As long as ratings are produced by the new blob splitting. When - * all the splitting has been accomplished all the ratings memory is - * reclaimed. + * Repeatedly chops the worst blob, classifying the new blobs fixing up all + * the data, and incrementally runs the segmentation search until a good word + * is found, or no more chops can be found. */ -void Wordrec::improve_by_chopping(WERD_RES *word, - BLOB_CHOICE_LIST_VECTOR *char_choices, - STATE *best_state, - BLOB_CHOICE_LIST_VECTOR *best_char_choices, - DANGERR *fixpt, - bool *best_choice_acceptable) { - inT32 blob_number; - float old_best; - bool updated_best_choice = false; - - while (1) { // improvement loop - old_best = word->best_choice->rating(); - if (improve_one_blob(word, char_choices, - &blob_number, &word->seam_array, - fixpt, (fragments_guide_chopper && - word->best_choice->fragment_mark()), - word->blamer_bundle)) { - getDict().LogNewSplit(blob_number); - updated_best_choice = - getDict().permute_characters(*char_choices, word->best_choice, - word->raw_choice); - - if (old_best > word->best_choice->rating()) { - set_n_ones(best_state, char_choices->length() - 1); +void Wordrec::improve_by_chopping(float rating_cert_scale, + WERD_RES* word, + BestChoiceBundle* best_choice_bundle, + BlamerBundle* blamer_bundle, + LMPainPoints* pain_points, + GenericVector* pending) { + int blob_number; + do { // improvement loop. + // Make a simple vector of BLOB_CHOICEs to make it easy to pick which + // one to chop. + GenericVector blob_choices; + int num_blobs = word->ratings->dimension(); + for (int i = 0; i < num_blobs; ++i) { + BLOB_CHOICE_LIST* choices = word->ratings->get(i, i); + if (choices == NULL || choices->empty()) { + blob_choices.push_back(NULL); } else { - insert_new_chunk(best_state, blob_number, char_choices->length() - 2); - fixpt->clear(); + BLOB_CHOICE_IT bc_it(choices); + blob_choices.push_back(bc_it.data()); } - - if (chop_debug) - print_state("best state = ", - best_state, count_blobs(word->chopped_word->blobs) - 1); - } else { - break; } - - // Check if we should break from the loop. - bool done = false; - bool replaced = false; - if ((updated_best_choice && - (*best_choice_acceptable = - getDict().AcceptableChoice(char_choices, word->best_choice, - fixpt, CHOPPER_CALLER, &replaced))) || - char_choices->length() >= MAX_NUM_CHUNKS) { - done = true; + SEAM* seam = improve_one_blob(blob_choices, &best_choice_bundle->fixpt, + false, false, word, &blob_number); + if (seam == NULL) break; + // A chop has been made. We have to correct all the data structures to + // take into account the extra bottom-level blob. + // Put the seam into the seam_array and correct everything else on the + // word: ratings matrix (including matrix location in the BLOB_CHOICES), + // states in WERD_CHOICEs, and blob widths. + word->InsertSeam(blob_number, seam); + // Insert a new entry in the beam array. + best_choice_bundle->beam.insert(new LanguageModelState, blob_number); + // Fixpts are outdated, but will get recalculated. + best_choice_bundle->fixpt.clear(); + // Remap existing pain points. + pain_points->RemapForSplit(blob_number); + // Insert a new pending at the chop point. + pending->insert(SegSearchPending(), blob_number); + + // Classify the two newly created blobs using ProcessSegSearchPainPoint, + // as that updates the pending correctly and adds new pain points. + MATRIX_COORD pain_point(blob_number, blob_number); + ProcessSegSearchPainPoint(0.0f, pain_point, "Chop1", pending, word, + pain_points, blamer_bundle); + pain_point.col = blob_number + 1; + pain_point.row = blob_number + 1; + ProcessSegSearchPainPoint(0.0f, pain_point, "Chop2", pending, word, + pain_points, blamer_bundle); + if (language_model_->language_model_ngram_on) { + // N-gram evaluation depends on the number of blobs in a chunk, so we + // have to re-evaluate everything in the word. + ResetNGramSearch(word, best_choice_bundle, pending); + blob_number = 0; } - if (replaced) update_blob_classifications(word->chopped_word, - *char_choices); - if (updated_best_choice) CopyCharChoices(*char_choices, best_char_choices); - if (done) break; + // Run language model incrementally. (Except with the n-gram model on.) + UpdateSegSearchNodes(rating_cert_scale, blob_number, pending, + word, pain_points, best_choice_bundle, blamer_bundle); + } while (!language_model_->AcceptableChoiceFound() && + word->ratings->dimension() < kMaxNumChunks); + + // If after running only the chopper best_choice is incorrect and no blame + // has been yet set, blame the classifier if best_choice is classifier's + // top choice and is a dictionary word (i.e. language model could not have + // helped). Otherwise blame the tradeoff between the classifier and + // the old language model (permuters). + if (word->blamer_bundle != NULL && + word->blamer_bundle->incorrect_result_reason() == IRR_CORRECT && + !word->blamer_bundle->ChoiceIsCorrect(word->best_choice)) { + bool valid_permuter = word->best_choice != NULL && + Dict::valid_word_permuter(word->best_choice->permuter(), false); + word->blamer_bundle->BlameClassifierOrLangModel(word, + getDict().getUnicharset(), + valid_permuter, + wordrec_debug_blamer); } } @@ -798,12 +570,10 @@ void Wordrec::improve_by_chopping(WERD_RES *word, * These are the results of the last classification. Find a likely * place to apply splits. If none, return -1. **********************************************************************/ -inT16 Wordrec::select_blob_to_split(const BLOB_CHOICE_LIST_VECTOR &char_choices, - float rating_ceiling, - bool split_next_to_fragment) { - BLOB_CHOICE_IT blob_choice_it; +int Wordrec::select_blob_to_split( + const GenericVector& blob_choices, + float rating_ceiling, bool split_next_to_fragment) { BLOB_CHOICE *blob_choice; - BLOB_CHOICE_IT temp_it; int x; float worst = -MAX_FLOAT32; int worst_index = -1; @@ -813,39 +583,36 @@ inT16 Wordrec::select_blob_to_split(const BLOB_CHOICE_LIST_VECTOR &char_choices, if (chop_debug) { if (rating_ceiling < MAX_FLOAT32) - cprintf("rating_ceiling = %8.4f\n", rating_ceiling); + tprintf("rating_ceiling = %8.4f\n", rating_ceiling); else - cprintf("rating_ceiling = No Limit\n"); + tprintf("rating_ceiling = No Limit\n"); } - if (split_next_to_fragment && char_choices.length() > 0) { - fragments = new const CHAR_FRAGMENT *[char_choices.length()]; - if (char_choices.get(0) != NULL) { - temp_it.set_to_list(char_choices.get(0)); + if (split_next_to_fragment && blob_choices.size() > 0) { + fragments = new const CHAR_FRAGMENT *[blob_choices.length()]; + if (blob_choices[0] != NULL) { fragments[0] = getDict().getUnicharset().get_fragment( - temp_it.data()->unichar_id()); + blob_choices[0]->unichar_id()); } else { fragments[0] = NULL; } } - for (x = 0; x < char_choices.length(); ++x) { - if (char_choices.get(x) == NULL) { + for (x = 0; x < blob_choices.size(); ++x) { + if (blob_choices[x] == NULL) { if (fragments != NULL) { delete[] fragments; } return x; } else { - blob_choice_it.set_to_list(char_choices.get(x)); - blob_choice = blob_choice_it.data(); + blob_choice = blob_choices[x]; // Populate fragments for the following position. - if (split_next_to_fragment && x+1 < char_choices.length()) { - if (char_choices.get(x+1) != NULL) { - temp_it.set_to_list(char_choices.get(x+1)); - fragments[x+1] = getDict().getUnicharset().get_fragment( - temp_it.data()->unichar_id()); + if (split_next_to_fragment && x+1 < blob_choices.size()) { + if (blob_choices[x + 1] != NULL) { + fragments[x + 1] = getDict().getUnicharset().get_fragment( + blob_choices[x + 1]->unichar_id()); } else { - fragments[x+1] = NULL; + fragments[x + 1] = NULL; } } if (blob_choice->rating() < rating_ceiling && @@ -858,7 +625,7 @@ inT16 Wordrec::select_blob_to_split(const BLOB_CHOICE_LIST_VECTOR &char_choices, if (split_next_to_fragment) { // Update worst_near_fragment and worst_index_near_fragment. bool expand_following_fragment = - (x + 1 < char_choices.length() && + (x + 1 < blob_choices.size() && fragments[x+1] != NULL && !fragments[x+1]->is_beginning()); bool expand_preceding_fragment = (x > 0 && fragments[x-1] != NULL && !fragments[x-1]->is_ending()); @@ -867,7 +634,7 @@ inT16 Wordrec::select_blob_to_split(const BLOB_CHOICE_LIST_VECTOR &char_choices, worst_index_near_fragment = x; worst_near_fragment = blob_choice->rating(); if (chop_debug) { - cprintf("worst_index_near_fragment=%d" + tprintf("worst_index_near_fragment=%d" " expand_following_fragment=%d" " expand_preceding_fragment=%d\n", worst_index_near_fragment, @@ -895,11 +662,11 @@ inT16 Wordrec::select_blob_to_split(const BLOB_CHOICE_LIST_VECTOR &char_choices, * dangerous blob that maps to multiple characters, return that blob * index as a place we need to split. If none, return -1. **********************************************************************/ -inT16 Wordrec::select_blob_to_split_from_fixpt(DANGERR *fixpt) { +int Wordrec::select_blob_to_split_from_fixpt(DANGERR *fixpt) { if (!fixpt) return -1; for (int i = 0; i < fixpt->size(); i++) { - if ((*fixpt)[i].begin == (*fixpt)[i].end && + if ((*fixpt)[i].begin + 1 == (*fixpt)[i].end && (*fixpt)[i].dangerous && (*fixpt)[i].correct_is_ngram) { return (*fixpt)[i].begin; @@ -908,136 +675,7 @@ inT16 Wordrec::select_blob_to_split_from_fixpt(DANGERR *fixpt) { return -1; } -/********************************************************************** - * set_chopper_blame - * - * Check whether chops were made at all the character bounding box boundaries - * in word->truth_word. If not - blame the chopper for an incorrect answer. - **********************************************************************/ -void Wordrec::set_chopper_blame(WERD_RES *word) { - BlamerBundle *blamer_bundle = word->blamer_bundle; - assert(blamer_bundle != NULL); - if (blamer_bundle->NoTruth() || !(blamer_bundle->truth_has_char_boxes) || - word->chopped_word->blobs == NULL) { - return; - } - STRING debug; - bool missing_chop = false; - TBLOB * curr_blob = word->chopped_word->blobs; - int b = 0; - inT16 truth_x; - while (b < blamer_bundle->truth_word.length() && curr_blob != NULL) { - truth_x = blamer_bundle->norm_truth_word.BlobBox(b).right(); - if (curr_blob->bounding_box().right() < - (truth_x - blamer_bundle->norm_box_tolerance)) { - curr_blob = curr_blob->next; - continue; // encountered an extra chop, keep looking - } else if (curr_blob->bounding_box().right() > - (truth_x + blamer_bundle->norm_box_tolerance)) { - missing_chop = true; - break; - } else { - curr_blob = curr_blob->next; - ++b; - } - } - if (missing_chop || b < blamer_bundle->norm_truth_word.length()) { - STRING debug; - char debug_buffer[256]; - if (missing_chop) { - sprintf(debug_buffer, "Detected missing chop (tolerance=%d) at ", - blamer_bundle->norm_box_tolerance); - debug += debug_buffer; - curr_blob->bounding_box().append_debug(&debug); - debug.add_str_int("\nNo chop for truth at x=", truth_x); - } else { - debug.add_str_int("Missing chops for last ", - blamer_bundle->norm_truth_word.length()-b); - debug += " truth box(es)"; - } - debug += "\nMaximally chopped word boxes:\n"; - for (curr_blob = word->chopped_word->blobs; curr_blob != NULL; - curr_blob = curr_blob->next) { - const TBOX &tbox = curr_blob->bounding_box(); - sprintf(debug_buffer, "(%d,%d)->(%d,%d)\n", - tbox.left(), tbox.bottom(), tbox.right(), tbox.top()); - debug += debug_buffer; - } - debug += "Truth bounding boxes:\n"; - for (b = 0; b < blamer_bundle->norm_truth_word.length(); ++b) { - const TBOX &tbox = blamer_bundle->norm_truth_word.BlobBox(b); - sprintf(debug_buffer, "(%d,%d)->(%d,%d)\n", - tbox.left(), tbox.bottom(), tbox.right(), tbox.top()); - debug += debug_buffer; - } - blamer_bundle->SetBlame(IRR_CHOPPER, debug, word->best_choice, - wordrec_debug_blamer); - } -} -/********************************************************************** - * word_associator - * - * Reassociate and classify the blobs in a word. Continue this process - * until a good answer is found or all the possibilities have been tried. - **********************************************************************/ -MATRIX *Wordrec::word_associator(bool only_create_ratings_matrix, - WERD_RES *word, - STATE *state, - BLOB_CHOICE_LIST_VECTOR *best_char_choices, - DANGERR *fixpt, - STATE *best_state) { - CHUNKS_RECORD chunks_record; - BLOB_WEIGHTS blob_weights; - int x; - int num_chunks; - BLOB_CHOICE_IT blob_choice_it; - - num_chunks = array_count(word->seam_array) + 1; - - TBLOB* blobs = word->chopped_word->blobs; - chunks_record.ratings = record_piece_ratings(blobs); - chunks_record.chunks = blobs; - chunks_record.word_res = word; - chunks_record.splits = word->seam_array; - chunks_record.chunk_widths = blobs_widths(blobs); - chunks_record.char_widths = blobs_widths(blobs); - /* Save chunk weights */ - for (x = 0; x < num_chunks; x++) { - BLOB_CHOICE_LIST* choices = get_piece_rating(chunks_record.ratings, blobs, - chunks_record.word_res->denorm, - word->seam_array, x, x, - word->blamer_bundle); - blob_choice_it.set_to_list(choices); - //This is done by Jetsoft. Divide by zero is possible. - if (blob_choice_it.data()->certainty() == 0) { - blob_weights[x]=0; - } else { - blob_weights[x] = - -(inT16) (10 * blob_choice_it.data()->rating() / - blob_choice_it.data()->certainty()); - } - } - chunks_record.weights = blob_weights; - - if (chop_debug) - chunks_record.ratings->print(getDict().getUnicharset()); - - if (!only_create_ratings_matrix) { - if (enable_new_segsearch) { - SegSearch(&chunks_record, word->best_choice, - best_char_choices, word->raw_choice, - state, word->blamer_bundle); - } else { - best_first_search(&chunks_record, best_char_choices, word, - state, fixpt, best_state); - } - } - - free_widths(chunks_record.chunk_widths); - free_widths(chunks_record.char_widths); - return chunks_record.ratings; -} } // namespace tesseract diff --git a/wordrec/chopper.h b/wordrec/chopper.h index 1ff1a13517..7955a51f1a 100644 --- a/wordrec/chopper.h +++ b/wordrec/chopper.h @@ -29,7 +29,6 @@ #include "cutil.h" #include "matrix.h" #include "seam.h" -#include "states.h" #include "stopper.h" @@ -41,7 +40,7 @@ EDGEPT *restore_outline(EDGEPT *start); void restore_outline_tree(TESSLINE *srcline); -int any_shared_split_points(SEAMS seam_list, SEAM *seam); +int any_shared_split_points(const GenericVector& seams, SEAM *seam); int check_blob(TBLOB *blob); diff --git a/wordrec/closed.cpp b/wordrec/closed.cpp deleted file mode 100644 index 86c684d893..0000000000 --- a/wordrec/closed.cpp +++ /dev/null @@ -1,130 +0,0 @@ -/* -*-C-*- - ******************************************************************************** - * - * File: closed.c (Formerly closed.c) - * Description: Hash table for closed search states. - * Author: Mark Seaman, OCR Technology - * Created: Fri Oct 16 14:37:00 1987 - * Modified: Fri May 25 11:31:16 1990 (Mark Seaman) marks@hpgrlt - * Language: C - * Package: N/A - * Status: Reusable Software Component - * - * (c) Copyright 1987, Hewlett-Packard Company. - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** http://www.apache.org/licenses/LICENSE-2.0 - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - * - *********************************************************************************/ -/*---------------------------------------------------------------------- - I n c l u d e s -----------------------------------------------------------------------*/ -#include "freelist.h" -#include "closed.h" -#include "cutil.h" -#include "callcpp.h" -#ifdef __UNIX__ -#include -#endif - -/*---------------------------------------------------------------------- - V a r i a b l e s -----------------------------------------------------------------------*/ -#define TABLE_SIZE 2000 - -/*---------------------------------------------------------------------- - F u n c t i o n s -----------------------------------------------------------------------*/ -/** - * @name hash_add - * - * Look in the hash table for a particular value. If it is not there - * then add it. - */ -int hash_add(HASH_TABLE state_table, STATE *state) { - int x; - int i = 0; - int table_limit = TABLE_SIZE; - - x = state->part2 % table_limit; - while (i < table_limit) { - assert (0 <= x && x < table_limit); - /* Found it */ - if ((state_table[x].part2 == state->part2) && - (state_table[x].part1 == state->part1)) { - return (FALSE); - } - /* Not in table */ - else if (state_table[x].part1 == NO_STATE) { - state_table[x].part2 = state->part2; - state_table[x].part1 = state->part1; - return (TRUE); - } - i++; - if (++x >= table_limit) - x = 0; - } - cprintf("warning: hash table is full"); - - abort(); - return 0; -} - - -/** - * @name hash_lookup - * - * Look in the hash table for a particular value. If the value is there - * then return TRUE, FALSE otherwise. - */ -int hash_lookup(HASH_TABLE state_table, STATE *state) { - int x; - int i = 0; - int table_limit = TABLE_SIZE; - - x = state->part2 % table_limit; - while (i < table_limit) { - assert (0 <= x && x < table_limit); - /* Found it */ - if ((state_table[x].part2 == state->part2) && - (state_table[x].part1 == state->part1)) { - return (TRUE); - } - /* Not in table */ - else if (state_table[x].part1 == NO_STATE) { - return (FALSE); - } - - i++; - if (++x >= table_limit) - x = 0; - } - cprintf ("warning: fell off end of hash table (%x) %x\n", - state->part2, state->part2 % table_limit); - abort(); - return 0; -} - - -/** - * @name new_hash_table - * - * Create and initialize a hash table. - */ -HASH_TABLE new_hash_table() { - HASH_TABLE ht; - int x; - - ht = (HASH_TABLE) memalloc (TABLE_SIZE * sizeof (STATE)); - for (x = 0; x < TABLE_SIZE; x++) { - ht[x].part1 = NO_STATE; - ht[x].part2 = NO_STATE; - } - return (ht); -} diff --git a/wordrec/closed.h b/wordrec/closed.h deleted file mode 100644 index 97851dc2af..0000000000 --- a/wordrec/closed.h +++ /dev/null @@ -1,43 +0,0 @@ -/* -*-C-*- - ******************************************************************************** - * - * File: closed.h (Formerly closed.h) - * Description: Hash table for closed search states. - * Author: Mark Seaman, SW Productivity - * Created: Fri Oct 16 14:37:00 1987 - * Modified: Fri May 25 11:27:11 1990 (Mark Seaman) marks@hpgrlt - * Language: C - * Package: N/A - * Status: Reusable Software Component - * - * (c) Copyright 1987, Hewlett-Packard Company. - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** http://www.apache.org/licenses/LICENSE-2.0 - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - * - *********************************************************************************/ - -#ifndef CLOSED_H -#define CLOSED_H - -#include -#include "states.h" - -typedef STATE *HASH_TABLE; -#define NO_STATE ~0 - -#define free_hash_table(table) memfree(table) - -int hash_add(HASH_TABLE state_table, STATE *state); - -int hash_lookup(HASH_TABLE state_table, STATE *state); - -HASH_TABLE new_hash_table(); - -#endif diff --git a/wordrec/findseam.cpp b/wordrec/findseam.cpp index 4c48fd1663..0db419eb3d 100644 --- a/wordrec/findseam.cpp +++ b/wordrec/findseam.cpp @@ -51,114 +51,40 @@ /* Evalute right away */ #define BAD_PRIORITY 9999.0 -/*---------------------------------------------------------------------- - M a c r o s -----------------------------------------------------------------------*/ -/********************************************************************** - * add_seam_to_queue - * - * Add this seam value to the seam queue. If the heap is already full - * then nothing is done. - **********************************************************************/ - -#define add_seam_to_queue(seams,seam,priority) \ -if (seam)\ -{\ - if (HeapFull(seams))\ - junk_worst_seam(seams,seam,priority);\ - else\ - HeapPush (seams, priority, (char*) seam);\ - } - -/********************************************************************** - * best_seam_priority - * - * Return the best priority value on the queue. - **********************************************************************/ - -#define best_seam_priority(seam_queue) \ -(HeapEmpty (seam_queue) ? \ - NO_FULL_PRIORITY : \ - ((SEAM*) seam_queue_element(seam_queue, 0))->priority) - -/********************************************************************** - * create_seam_queue - * - * Create a new seam queue with no elements in it. - **********************************************************************/ - -#define create_seam_queue(seam_queue) \ -(seam_queue = MakeHeap (MAX_NUM_SEAMS)) - -/********************************************************************** - * create_seam_pile - * - * Create a new seam pile with no elements in it. - **********************************************************************/ - -#define create_seam_pile(seam_pile) \ -(seam_pile = array_new (MAX_OLD_SEAMS)) - -/********************************************************************** - * delete_seam_queue - * - * Delete a seam queue along with all the seam structures associated - * with it. - **********************************************************************/ - -#define delete_seam_queue(seam_queue) \ -(FreeHeapData (seam_queue, delete_seam), \ - seam_queue = NULL) \ - - -/********************************************************************** - * pop_next_seam - * - * Remove the next seam from the queue. Put the seam and priority - * values in the requested variables. If there was nothing to pop - * then return FALSE, else return TRUE. - **********************************************************************/ - -#define pop_next_seam(seams,seam,priority) \ -(HeapPop (seams,&priority,&seam) == TESS_HEAP_OK) \ - - -/********************************************************************** - * seam_queue_element - * - * Return the element from the seam queue at the requested index. - **********************************************************************/ - -#define seam_queue_element(seam_queue,index) \ -((index < SizeOfHeap (seam_queue)) ? \ - HeapDataFor (seam_queue, index) : \ - NULL) \ - - /*---------------------------------------------------------------------- F u n c t i o n s ----------------------------------------------------------------------*/ namespace tesseract { /********************************************************************** - * junk_worst_seam + * add_seam_to_queue * - * Delete the worst seam from the queue because it is full. + * Adds the given new_seam to the seams priority queue, unless it is full + * and the new seam is worse than the worst. **********************************************************************/ -void Wordrec::junk_worst_seam(SEAM_QUEUE seams, SEAM *new_seam, - float new_priority) { - SEAM *seam; - float priority; - - HeapPopWorst(seams, &priority, &seam); - if (priority > new_priority) { - delete_seam(seam); /*get rid of it */ - HeapPush (seams, new_priority, (char *) new_seam); +void Wordrec::add_seam_to_queue(float new_priority, SEAM *new_seam, + SeamQueue* seams) { + if (new_seam == NULL) return; + if (chop_debug) { + tprintf("Pushing new seam with priority %g :", new_priority); + print_seam("seam: ", new_seam); } - else { - delete_seam(new_seam); - HeapPush (seams, priority, (char *) seam); + if (seams->size() >= MAX_NUM_SEAMS) { + SeamPair old_pair(0, NULL); + if (seams->PopWorst(&old_pair) && old_pair.key() <= new_priority) { + if (chop_debug) { + tprintf("Old seam staying with priority %g\n", old_pair.key()); + } + delete new_seam; + seams->Push(&old_pair); + return; + } else if (chop_debug) { + tprintf("New seam with priority %g beats old worst seam with %g\n", + new_priority, old_pair.key()); + } } + SeamPair new_pair(new_priority, new_seam); + seams->Push(&new_pair); } @@ -175,12 +101,12 @@ void Wordrec::junk_worst_seam(SEAM_QUEUE seams, SEAM *new_seam, * a split of NULL, then no further splits can be supplied by the * caller. **********************************************************************/ -void Wordrec::choose_best_seam(SEAM_QUEUE seam_queue, - SEAM_PILE *seam_pile, +void Wordrec::choose_best_seam(SeamQueue* seam_queue, SPLIT *split, PRIORITY priority, SEAM **seam_result, - TBLOB *blob) { + TBLOB *blob, + GenericVector* seam_pile) { SEAM *seam; char str[80]; float my_priority; @@ -190,10 +116,10 @@ void Wordrec::choose_best_seam(SEAM_QUEUE seam_queue, TPOINT split_point = split->point1->pos; split_point += split->point2->pos; split_point /= 2; - seam = new_seam(my_priority, split_point, split, NULL, NULL); + seam = new SEAM(my_priority, split_point, split, NULL, NULL); if (chop_debug > 1) print_seam ("Partial priority ", seam); - add_seam_to_queue (seam_queue, seam, (float) my_priority); + add_seam_to_queue(my_priority, seam, seam_queue); if (my_priority > chop_good_split) return; @@ -201,9 +127,12 @@ void Wordrec::choose_best_seam(SEAM_QUEUE seam_queue, TBOX bbox = blob->bounding_box(); /* Queue loop */ - while (pop_next_seam (seam_queue, seam, my_priority)) { + while (!seam_queue->empty()) { + SeamPair seam_pair; + seam_queue->Pop(&seam_pair); + seam = seam_pair.extract_data(); /* Set full priority */ - my_priority = seam_priority (seam, bbox.left(), bbox.right()); + my_priority = seam_priority(seam, bbox.left(), bbox.right()); if (chop_debug) { sprintf (str, "Full my_priority %0.0f, ", my_priority); print_seam(str, seam); @@ -213,12 +142,12 @@ void Wordrec::choose_best_seam(SEAM_QUEUE seam_queue, (*seam_result)->priority > my_priority) && my_priority < chop_ok_split) { /* No crossing */ if (constrained_split (seam->split1, blob)) { - delete_seam(*seam_result); - clone_seam(*seam_result, seam); + delete *seam_result; + *seam_result = new SEAM(*seam); (*seam_result)->priority = my_priority; } else { - delete_seam(seam); + delete seam; seam = NULL; my_priority = BAD_PRIORITY; } @@ -226,22 +155,23 @@ void Wordrec::choose_best_seam(SEAM_QUEUE seam_queue, if (my_priority < chop_good_split) { if (seam) - delete_seam(seam); + delete seam; return; /* Made good answer */ } if (seam) { /* Combine with others */ - if (array_count (*seam_pile) < MAX_NUM_SEAMS + if (seam_pile->size() < MAX_NUM_SEAMS /*|| tessedit_truncate_chopper==0 */ ) { - combine_seam(seam_queue, *seam_pile, seam); - *seam_pile = array_push (*seam_pile, seam); + combine_seam(*seam_pile, seam, seam_queue); + seam_pile->push_back(seam); } else - delete_seam(seam); + delete seam; } - my_priority = best_seam_priority (seam_queue); + my_priority = seam_queue->empty() ? NO_FULL_PRIORITY + : seam_queue->PeekTop().key(); if ((my_priority > chop_ok_split) || (my_priority > chop_good_split && split)) return; @@ -256,8 +186,8 @@ void Wordrec::choose_best_seam(SEAM_QUEUE seam_queue, * from this union should be added to the seam queue. The return value * tells whether or not any additional seams were added to the queue. **********************************************************************/ -void Wordrec::combine_seam(SEAM_QUEUE seam_queue, SEAM_PILE seam_pile, - SEAM *seam) { +void Wordrec::combine_seam(const GenericVector& seam_pile, + const SEAM* seam, SeamQueue* seam_queue) { register inT16 x; register inT16 dist; inT16 bottom1, top1; @@ -286,8 +216,8 @@ void Wordrec::combine_seam(SEAM_QUEUE seam_queue, SEAM_PILE seam_pile, bottom2 = bottom1; top2 = top1; } - array_loop(seam_pile, x) { - this_one = (SEAM *) array_value (seam_pile, x); + for (int x = 0; x < seam_pile.size(); ++x) { + this_one = seam_pile[x]; dist = seam->location.x - this_one->location.x; if (-SPLIT_CLOSENESS < dist && dist < SPLIT_CLOSENESS && @@ -325,9 +255,11 @@ void Wordrec::combine_seam(SEAM_QUEUE seam_queue, SEAM_PILE seam_pile, ) ) { new_one = join_two_seams (seam, this_one); - if (chop_debug > 1) - print_seam ("Combo priority ", new_one); - add_seam_to_queue (seam_queue, new_one, new_one->priority); + if (new_one != NULL) { + if (chop_debug > 1) + print_seam ("Combo priority ", new_one); + add_seam_to_queue(new_one->priority, new_one, seam_queue); + } } } } @@ -355,22 +287,6 @@ inT16 Wordrec::constrained_split(SPLIT *split, TBLOB *blob) { return (TRUE); } - -/********************************************************************** - * delete_seam_pile - * - * Delete the seams that are held in the seam pile. Destroy the splits - * that are referenced by these seams. - **********************************************************************/ -void Wordrec::delete_seam_pile(SEAM_PILE seam_pile) { - inT16 x; - - array_loop(seam_pile, x) { - delete_seam ((SEAM *) array_value (seam_pile, x)); - } - array_free(seam_pile); -} - /********************************************************************** * pick_good_seam * @@ -378,11 +294,7 @@ void Wordrec::delete_seam_pile(SEAM_PILE seam_pile) { * Work from the outlines provided. **********************************************************************/ SEAM *Wordrec::pick_good_seam(TBLOB *blob) { - SEAM_QUEUE seam_queue; - SEAM_PILE seam_pile; - POINT_GROUP point_heap; - PRIORITY priority; - EDGEPT *edge; + GenericVector seam_pile; EDGEPT *points[MAX_NUM_POINTS]; EDGEPT_CLIST new_points; SEAM *seam = NULL; @@ -396,30 +308,28 @@ SEAM *Wordrec::pick_good_seam(TBLOB *blob) { draw_blob_edges(blob); #endif - point_heap = MakeHeap (MAX_NUM_POINTS); + PointHeap point_heap(MAX_NUM_POINTS); for (outline = blob->outlines; outline; outline = outline->next) - prioritize_points(outline, point_heap); + prioritize_points(outline, &point_heap); - while (HeapPop (point_heap, &priority, &edge) == TESS_HEAP_OK) { - if (num_points < MAX_NUM_POINTS) - points[num_points++] = (EDGEPT *) edge; + while (!point_heap.empty() && num_points < MAX_NUM_POINTS) { + points[num_points++] = point_heap.PeekTop().data; + point_heap.Pop(NULL); } - FreeHeap(point_heap); - /* Initialize queue & pile */ - create_seam_pile(seam_pile); - create_seam_queue(seam_queue); + /* Initialize queue */ + SeamQueue seam_queue(MAX_NUM_SEAMS); - try_point_pairs(points, num_points, seam_queue, &seam_pile, &seam, blob); + try_point_pairs(points, num_points, &seam_queue, &seam_pile, &seam, blob); try_vertical_splits(points, num_points, &new_points, - seam_queue, &seam_pile, &seam, blob); + &seam_queue, &seam_pile, &seam, blob); if (seam == NULL) { - choose_best_seam(seam_queue, &seam_pile, NULL, BAD_PRIORITY, &seam, blob); + choose_best_seam(&seam_queue, NULL, BAD_PRIORITY, &seam, blob, &seam_pile); } else if (seam->priority > chop_good_split) { - choose_best_seam (seam_queue, &seam_pile, NULL, seam->priority, - &seam, blob); + choose_best_seam(&seam_queue, NULL, seam->priority, + &seam, blob, &seam_pile); } EDGEPT_C_IT it(&new_points); @@ -435,12 +345,11 @@ SEAM *Wordrec::pick_good_seam(TBLOB *blob) { } } - delete_seam_queue(seam_queue); - delete_seam_pile(seam_pile); + seam_pile.delete_data_pointers(); if (seam) { if (seam->priority > chop_ok_split) { - delete_seam(seam); + delete seam; seam = NULL; } #ifndef GRAPHICS_DISABLED @@ -509,12 +418,12 @@ PRIORITY Wordrec::seam_priority(SEAM *seam, inT16 xmin, inT16 xmax) { * together. See if any of them are suitable for use. Use a seam * queue and seam pile that have already been initialized and used. **********************************************************************/ -void Wordrec::try_point_pairs (EDGEPT * points[MAX_NUM_POINTS], - inT16 num_points, - SEAM_QUEUE seam_queue, - SEAM_PILE * seam_pile, - SEAM ** seam, - TBLOB * blob) { +void Wordrec::try_point_pairs(EDGEPT * points[MAX_NUM_POINTS], + inT16 num_points, + SeamQueue* seam_queue, + GenericVector* seam_pile, + SEAM ** seam, + TBLOB * blob) { inT16 x; inT16 y; SPLIT *split; @@ -533,11 +442,10 @@ void Wordrec::try_point_pairs (EDGEPT * points[MAX_NUM_POINTS], split = new_split (points[x], points[y]); priority = partial_split_priority (split); - choose_best_seam(seam_queue, seam_pile, split, priority, seam, blob); + choose_best_seam(seam_queue, split, priority, seam, blob, seam_pile); } } } - } @@ -554,8 +462,8 @@ void Wordrec::try_point_pairs (EDGEPT * points[MAX_NUM_POINTS], void Wordrec::try_vertical_splits(EDGEPT * points[MAX_NUM_POINTS], inT16 num_points, EDGEPT_CLIST *new_points, - SEAM_QUEUE seam_queue, - SEAM_PILE * seam_pile, + SeamQueue* seam_queue, + GenericVector* seam_pile, SEAM ** seam, TBLOB * blob) { EDGEPT *vertical_point = NULL; @@ -580,7 +488,7 @@ void Wordrec::try_vertical_splits(EDGEPT * points[MAX_NUM_POINTS], split = new_split (points[x], vertical_point); priority = partial_split_priority (split); - choose_best_seam(seam_queue, seam_pile, split, priority, seam, blob); + choose_best_seam(seam_queue, split, priority, seam, blob, seam_pile); } } } diff --git a/wordrec/findseam.h b/wordrec/findseam.h index d92121de0e..07f66bd931 100644 --- a/wordrec/findseam.h +++ b/wordrec/findseam.h @@ -30,10 +30,12 @@ I n c l u d e s ----------------------------------------------------------------------*/ #include "seam.h" -#include "oldheap.h" +#include "genericheap.h" +#include "kdpair.h" #include "chop.h" -typedef HEAP *SEAM_QUEUE; -typedef ARRAY SEAM_PILE; +// The SeamPair elements own their SEAMs and delete them upon destruction. +typedef tesseract::KDPtrPairInc SeamPair; +typedef tesseract::GenericHeap SeamQueue; #endif diff --git a/wordrec/gradechop.cpp b/wordrec/gradechop.cpp index e3410b4b2b..dce35ba5fb 100644 --- a/wordrec/gradechop.cpp +++ b/wordrec/gradechop.cpp @@ -94,8 +94,15 @@ PRIORITY Wordrec::full_split_priority(SPLIT *split, inT16 xmin, inT16 xmax) { **********************************************************************/ PRIORITY Wordrec::grade_center_of_blob(register BOUNDS_RECT rect) { register PRIORITY grade; + int width1 = rect[1] - rect[0]; + int width2 = rect[3] - rect[2]; - grade = (rect[1] - rect[0]) - (rect[3] - rect[2]); + if (width1 > chop_centered_maxwidth && + width2 > chop_centered_maxwidth) { + return 0.0; + } + + grade = width1 - width2; if (grade < 0) grade = -grade; diff --git a/wordrec/heuristic.cpp b/wordrec/heuristic.cpp deleted file mode 100644 index 8972f1bbd5..0000000000 --- a/wordrec/heuristic.cpp +++ /dev/null @@ -1,342 +0,0 @@ -/* -*-C-*- - ******************************************************************************** - * - * File: heuristic.c (Formerly heuristic.c) - * Description: - * Author: Mark Seaman, OCR Technology - * Created: Fri Oct 16 14:37:00 1987 - * Modified: Wed Jul 10 14:15:08 1991 (Mark Seaman) marks@hpgrlt - * Language: C - * Package: N/A - * Status: Reusable Software Component - * - * (c) Copyright 1987, Hewlett-Packard Company. - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** http://www.apache.org/licenses/LICENSE-2.0 - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - * - *********************************************************************************/ -/*---------------------------------------------------------------------- - I n c l u d e s -----------------------------------------------------------------------*/ -#include - -// Note: "heuristic.h" is an empty file and deleted -#include "associate.h" -#include "bestfirst.h" -#include "seam.h" -#include "baseline.h" -#include "freelist.h" -#include "measure.h" -#include "ratngs.h" -#include "wordrec.h" - -/*---------------------------------------------------------------------- - M a c r o s -----------------------------------------------------------------------*/ -#define BAD_RATING 1000.0 /* No valid blob */ - - -namespace tesseract { - -/*---------------------------------------------------------------------- -// Some static helpers used only in this file -----------------------------------------------------------------------*/ - -// Return a character width record corresponding to the character -// width that will be generated in this segmentation state. -// The calling function need to memfree WIDTH_RECORD when finished. -// This is the same as the original function, only cosmetic changes, -// except instead of passing chunks back to be freed, it deallocates -// internally. -WIDTH_RECORD *Wordrec::state_char_widths(WIDTH_RECORD *chunk_widths, - STATE *state, - int num_joints) { - SEARCH_STATE chunks = bin_to_chunks(state, num_joints); - int num_chars = chunks[0] + 1; - - // allocate and store (n+1,w0,g0,w1,g1...,wn) in int[2*(n+1)] as a - // struct { num_chars, widths[2*n+1]; } - WIDTH_RECORD *char_widths = (WIDTH_RECORD*) memalloc(sizeof(int)*num_chars*2); - char_widths->num_chars = num_chars; - - int first_blob = 0; - int last_blob; - for (int i = 1; i <= num_chars; i++) { - last_blob = (i > chunks[0]) ? num_joints : first_blob + chunks[i]; - - char_widths->widths[2*i-2] = - AssociateUtils::GetChunksWidth(chunk_widths, first_blob, last_blob); - if (i <= chunks[0]) { - char_widths->widths[2*i-1] = - AssociateUtils::GetChunksGap(chunk_widths, last_blob); - } - - if (segment_adjust_debug > 3) - tprintf("width_record[%d]s%d--s%d(%d) %d %d:%d\n", - i-1, first_blob, last_blob, chunks[i], - char_widths->widths[2*i-2], char_widths->widths[2*i-1], - chunk_widths->widths[2*last_blob+1]); - first_blob = last_blob + 1; - } - - memfree(chunks); - return char_widths; -} - -// Computes the variance of the char widths normalized to the given height -// TODO(dsl): Do this in a later stage and use char choice info to skip -// punctuations. -FLOAT32 Wordrec::get_width_variance(WIDTH_RECORD *wrec, float norm_height) { - MEASUREMENT ws; - new_measurement(ws); - for (int x = 0; x < wrec->num_chars; x++) { - FLOAT32 wh_ratio = wrec->widths[2 * x] * 1.0f / norm_height; - if (x == wrec->num_chars - 1 && wh_ratio > 0.3) - continue; // exclude trailing punctuation from stats - ADD_SAMPLE(ws, wh_ratio); - } - if (segment_adjust_debug > 2) - tprintf("Width Mean=%g Var=%g\n", MEAN(ws), VARIANCE(ws)); - return VARIANCE(ws); -} - -// Computes the variance of char positioning (width + spacing) wrt norm_height -FLOAT32 Wordrec::get_gap_variance(WIDTH_RECORD *wrec, float norm_height) { - MEASUREMENT ws; - new_measurement(ws); - for (int x = 0; x < wrec->num_chars - 1; x++) { - FLOAT32 gap_ratio = (wrec->widths[2 * x] + wrec->widths[ 2*x + 1]) - * 1.0 / norm_height; - ADD_SAMPLE(ws, gap_ratio); - } - if (segment_adjust_debug > 2) - tprintf("Gap Mean=%g Var=%g\n", MEAN(ws), VARIANCE(ws)); - return VARIANCE(ws); -} - - -/*---------------------------------------------------------------------- - Below are the new state prioritization functions, which incorporates - segmentation cost and width distribution for fixed pitch languages. - They are included as methods in class Wordrec to obtain more context. - ----------------------------------------------------------------------*/ - -/********************************************************************** - * Returns the cost associated with the segmentation state. - * - * The number of states should be the same as the number of seams. - * If state[i] is 1, then seams[i] is present; otherwise, it is hidden. - * This function returns the sum of priority for active seams. - * TODO(dsl): To keep this clean, this function will just return the - * sum of raw "priority" as seam cost. The normalization of this score - * relative to other costs will be done in bestfirst.cpp evaluate_seg(). - **********************************************************************/ - -FLOAT32 Wordrec::seamcut_priority(SEAMS seams, - STATE *state, - int num_joints) { - int x; - unsigned int mask = (num_joints > 32) ? (1 << (num_joints - 1 - 32)) - : (1 << (num_joints - 1)); - float seam_cost = 0.0f; - for (x = num_joints - 1; x >= 0; x--) { - int i = num_joints - 1 - x; - uinT32 value = (x < 32) ? state->part2 : state->part1; - bool state_on = value & mask; - if (state_on) { - SEAM* seam = (SEAM *) array_value(seams, i); - seam_cost += seam->priority; - } - if (mask == 1) - mask = 1 << 31; - else - mask >>= 1; - } - if (segment_adjust_debug > 2) - tprintf("seam_cost: %f\n", seam_cost); - return seam_cost; -} - -/********************************************************************** - * rating_priority - * - * Assign a segmentation priority based on the ratings of the blobs - * (in that segmentation) that have been classified. The average - * "goodness" (i.e. rating / weight) for each blob is used to indicate - * the segmentation priority. This is the original. - **********************************************************************/ -FLOAT32 Wordrec::rating_priority(CHUNKS_RECORD *chunks_record, - STATE *state, - int num_joints) { - BLOB_CHOICE_LIST *blob_choices; - BLOB_CHOICE_IT blob_choice_it; - inT16 first_chunk = 0; - inT16 last_chunk; - inT16 ratings = 0; - inT16 weights = 0; - - PIECES_STATE blob_chunks; - bin_to_pieces(state, num_joints, blob_chunks); - - for (int x = 0; blob_chunks[x]; x++) { - last_chunk = first_chunk + blob_chunks[x]; - - blob_choices = chunks_record->ratings->get(first_chunk, last_chunk - 1); - if (blob_choices != NOT_CLASSIFIED && blob_choices->length() > 0) { - blob_choice_it.set_to_list(blob_choices); - ratings += (inT16) blob_choice_it.data()->rating(); - for (int y = first_chunk; y < last_chunk; y++) { - weights += (inT16) (chunks_record->weights[y]); - } - } - first_chunk = last_chunk; - } - if (weights <= 0) - weights = 1; - FLOAT32 rating_cost = static_cast(ratings) / - static_cast(weights); - if (segment_adjust_debug > 2) - tprintf("rating_cost: r%f / w%f = %f\n", ratings, weights, rating_cost); - return rating_cost; -} - -/********************************************************************** - * width_priority - * - * Return a priority value for this word segmentation based on the - * character widths present in the new segmentation. - * For variable-pitch fonts, this should do the same thing as before: - * ie. penalize only on really wide squatting blobs. - * For fixed-pitch fonts, this will include a measure of char & gap - * width consistency. - * TODO(dsl): generalize this to use a PDF estimate for proportional and - * fixed pitch mode. - **********************************************************************/ -FLOAT32 Wordrec::width_priority(CHUNKS_RECORD *chunks_record, - STATE *state, - int num_joints) { - FLOAT32 penalty = 0.0; - WIDTH_RECORD *width_rec = state_char_widths(chunks_record->chunk_widths, - state, num_joints); - // When baseline_enable==True, which is the current default for Tesseract, - // a fixed value of 128 (BASELINE_SCALE) is always used. - FLOAT32 normalizing_height = BASELINE_SCALE; - if (assume_fixed_pitch_char_segment) { - // For fixed pitch language like CJK, we use the full text height as the - // normalizing factor so we are not dependent on xheight calculation. - // In the normalized coord. xheight * scale == BASELINE_SCALE(128), - // so add proportionally scaled ascender zone to get full text height. - const DENORM& denorm = chunks_record->word_res->denorm; - normalizing_height = denorm.y_scale() * - (denorm.row()->x_height() + denorm.row()->ascenders()); - if (segment_adjust_debug > 1) - tprintf("WidthPriority: %f %f normalizing height = %f\n", - denorm.row()->x_height(), denorm.row()->ascenders(), - normalizing_height); - // Impose additional segmentation penalties if blob widths or gaps - // distribution don't fit a fixed-pitch model. - FLOAT32 width_var = get_width_variance(width_rec, normalizing_height); - FLOAT32 gap_var = get_gap_variance(width_rec, normalizing_height); - penalty += width_var; - penalty += gap_var; - } - - for (int x = 0; x < width_rec->num_chars; x++) { - FLOAT32 squat = width_rec->widths[2*x]; - FLOAT32 gap = (x < width_rec->num_chars-1) ? width_rec->widths[2*x+1] : 0; - squat /= normalizing_height; - gap /= normalizing_height; - if (assume_fixed_pitch_char_segment) { - penalty += AssociateUtils::FixedPitchWidthCost( - squat, 0.0f, x == 0 || x == width_rec->num_chars -1, - heuristic_max_char_wh_ratio); - penalty += AssociateUtils::FixedPitchGapCost( - gap, x == width_rec->num_chars - 1); - if (width_rec->num_chars == 1 && - squat > AssociateUtils::kMaxFixedPitchCharAspectRatio) { - penalty += 10; - } - } else { - // Original equation when - // heuristic_max_char_ratio == AssociateUtils::kMaxSquat - if (squat > heuristic_max_char_wh_ratio) - penalty += squat - heuristic_max_char_wh_ratio; - } - } - - free_widths(width_rec); - return (penalty); -} - - -/********************************************************************** - * prioritize_state - * - * Create a priority for this state. It represents the urgency of - * checking this state. The larger the priority value, the worse the - * state is (lower priority). The "value" of this priority should be - * somewhat consistent with the final word rating. - * The question is how to normalize the different scores, and adjust - * the relative importance among them. - **********************************************************************/ -FLOAT32 Wordrec::prioritize_state(CHUNKS_RECORD *chunks_record, - SEARCH_RECORD *the_search) { - FLOAT32 shape_cost; - FLOAT32 width_cost; - FLOAT32 seam_cost; - - shape_cost = rating_priority(chunks_record, - the_search->this_state, - the_search->num_joints); - - width_cost = width_priority(chunks_record, - the_search->this_state, - the_search->num_joints); - - // The rating_priority is the same as the original, and the width_priority - // is the same as before if assume_fixed_pitch_char_segment == FALSE. - // So this would return the original state priority. - if (!use_new_state_cost) - return width_cost * 1000 + shape_cost; - - seam_cost = seamcut_priority(chunks_record->splits, - the_search->this_state, - the_search->num_joints); - - // TODO(dsl): how do we normalize the scores for these separate evidence? - // FLOAT32 total_cost = shape_cost + width_cost * 0.01 + seam_cost * 0.001; - FLOAT32 total_cost = shape_cost * heuristic_weight_rating + - width_cost * heuristic_weight_width + - seam_cost * heuristic_weight_seamcut; - - // We don't have an adjustment model for variable pitch segmentation cost - // into word rating - if (assume_fixed_pitch_char_segment) { - float seg_bias = 1.0; - if (width_cost < 1) seg_bias *= 0.85; - if (width_cost > 3) - seg_bias *= pow(heuristic_segcost_rating_base, width_cost/3.0); - if (seam_cost > 10) - seg_bias *= pow(heuristic_segcost_rating_base, log(seam_cost)/log(10.0)); - if (shape_cost > 5) - seg_bias *= pow(heuristic_segcost_rating_base, shape_cost/5.0); - if (segment_adjust_debug) { - tprintf("SegCost: %g Weight: %g rating: %g width: %g seam: %g\n", - total_cost, seg_bias, shape_cost, width_cost, seam_cost); - } - the_search->segcost_bias = seg_bias; - } else { - the_search->segcost_bias = 0; - } - - return total_cost; -} - -} // namespace tesseract diff --git a/wordrec/language_model.cpp b/wordrec/language_model.cpp index 3cad7aac3e..83f7e64710 100644 --- a/wordrec/language_model.cpp +++ b/wordrec/language_model.cpp @@ -23,22 +23,18 @@ #include "language_model.h" #include "dawg.h" +#include "freelist.h" #include "intproto.h" +#include "helpers.h" +#include "lm_state.h" +#include "lm_pain_points.h" #include "matrix.h" #include "params.h" #include "params_training_featdef.h" namespace tesseract { -ELISTIZE(ViterbiStateEntry); - -const float LanguageModel::kInitialPainPointPriorityAdjustment = 5.0f; -const float LanguageModel::kDefaultPainPointPriorityAdjustment = 2.0f; -const float LanguageModel::kBestChoicePainPointPriorityAdjustment = 0.5f; -const float LanguageModel::kCriticalPainPointPriorityAdjustment = 0.1f; const float LanguageModel::kMaxAvgNgramCost = 25.0f; -const int LanguageModel::kMinFixedLengthDawgLength = 2; -const float LanguageModel::kLooseMaxCharWhRatio = 2.5f; LanguageModel::LanguageModel(const UnicityTable *fontinfo_table, Dict *dict) @@ -73,16 +69,16 @@ LanguageModel::LanguageModel(const UnicityTable *fontinfo_table, "Strength of the character ngram model relative to the" " character classifier ", dict->getImage()->getCCUtil()->params()), + double_MEMBER(language_model_ngram_rating_factor, 16.0, + "Factor to bring log-probs into the same range as ratings" + " when multiplied by outline length ", + dict->getImage()->getCCUtil()->params()), BOOL_MEMBER(language_model_ngram_space_delimited_language, true, "Words are delimited by space", dict->getImage()->getCCUtil()->params()), INT_MEMBER(language_model_min_compound_length, 3, "Minimum length of compound words", dict->getImage()->getCCUtil()->params()), - INT_MEMBER(language_model_fixed_length_choices_depth, 3, - "Depth of blob choice lists to explore" - " when fixed length dawgs are on", - dict->getImage()->getCCUtil()->params()), double_MEMBER(language_model_penalty_non_freq_dict_word, 0.1, "Penalty for words not in the frequent word dictionary", dict->getImage()->getCCUtil()->params()), @@ -112,6 +108,8 @@ LanguageModel::LanguageModel(const UnicityTable *fontinfo_table, double_MEMBER(language_model_penalty_increment, 0.01, "Penalty increment", dict->getImage()->getCCUtil()->params()), + INT_MEMBER(wordrec_display_segmentations, 0, "Display Segmentations", + dict->getImage()->getCCUtil()->params()), BOOL_INIT_MEMBER(language_model_use_sigmoidal_certainty, false, "Use sigmoidal score for certainty", dict->getImage()->getCCUtil()->params()), @@ -119,74 +117,32 @@ LanguageModel::LanguageModel(const UnicityTable *fontinfo_table, fixed_pitch_(false), max_char_wh_ratio_(0.0), acceptable_choice_found_(false) { ASSERT_HOST(dict_ != NULL); - dawg_args_ = new DawgArgs(NULL, NULL, new DawgInfoVector(), - new DawgInfoVector(), - 0.0, NO_PERM, kAnyWordLength, -1); - beginning_active_dawgs_ = new DawgInfoVector(); - beginning_constraints_ = new DawgInfoVector(); - fixed_length_beginning_active_dawgs_ = new DawgInfoVector(); - empty_dawg_info_vec_ = new DawgInfoVector(); + dawg_args_ = new DawgArgs(NULL, new DawgPositionVector(), NO_PERM); + very_beginning_active_dawgs_ = new DawgPositionVector(); + beginning_active_dawgs_ = new DawgPositionVector(); } LanguageModel::~LanguageModel() { + delete very_beginning_active_dawgs_; delete beginning_active_dawgs_; - delete beginning_constraints_; - delete fixed_length_beginning_active_dawgs_; - delete empty_dawg_info_vec_; - delete dawg_args_->updated_active_dawgs; - delete dawg_args_->updated_constraints; + delete dawg_args_->updated_dawgs; delete dawg_args_; } -void LanguageModel::InitForWord( - const WERD_CHOICE *prev_word, - bool fixed_pitch, float best_choice_cert, float max_char_wh_ratio, - float rating_cert_scale, HEAP *pain_points, CHUNKS_RECORD *chunks_record, - BlamerBundle *blamer_bundle, bool debug_blamer) { +void LanguageModel::InitForWord(const WERD_CHOICE *prev_word, + bool fixed_pitch, float max_char_wh_ratio, + float rating_cert_scale) { fixed_pitch_ = fixed_pitch; max_char_wh_ratio_ = max_char_wh_ratio; rating_cert_scale_ = rating_cert_scale; acceptable_choice_found_ = false; correct_segmentation_explored_ = false; - // For each cell, generate a "pain point" if the cell is not classified - // and has a left or right neighbor that was classified. - MATRIX *ratings = chunks_record->ratings; - for (int col = 0; col < ratings->dimension(); ++col) { - for (int row = col+1; row < ratings->dimension(); ++row) { - if ((row > 0 && ratings->get(col, row-1) != NOT_CLASSIFIED) || - (col+1 < ratings->dimension() && - ratings->get(col+1, row) != NOT_CLASSIFIED)) { - float worst_piece_cert; - bool fragmented; - GetWorstPieceCertainty(col, row, chunks_record->ratings, - &worst_piece_cert, &fragmented); - GeneratePainPoint(col, row, true, kInitialPainPointPriorityAdjustment, - worst_piece_cert, fragmented, best_choice_cert, - max_char_wh_ratio_, NULL, NULL, - chunks_record, pain_points); - } - } - } - // Initialize vectors with beginning DawgInfos. + very_beginning_active_dawgs_->clear(); + dict_->init_active_dawgs(very_beginning_active_dawgs_, false); beginning_active_dawgs_->clear(); - dict_->init_active_dawgs(kAnyWordLength, beginning_active_dawgs_, false); - beginning_constraints_->clear(); - dict_->init_constraints(beginning_constraints_); - if (dict_->GetMaxFixedLengthDawgIndex() >= 0) { - fixed_length_beginning_active_dawgs_->clear(); - for (int i = 0; i < beginning_active_dawgs_->size(); ++i) { - int dawg_index = (*beginning_active_dawgs_)[i].dawg_index; - if (dawg_index <= dict_->GetMaxFixedLengthDawgIndex() && - dawg_index >= kMinFixedLengthDawgLength) { - *fixed_length_beginning_active_dawgs_ += (*beginning_active_dawgs_)[i]; - } - } - } - - max_penalty_adjust_ = (dict_->segment_penalty_dict_nonword - - dict_->segment_penalty_dict_case_ok); + dict_->default_dawgs(beginning_active_dawgs_, false); // Fill prev_word_str_ with the last language_model_ngram_order // unichars from prev_word. @@ -195,7 +151,7 @@ void LanguageModel::InitForWord( prev_word_str_ = prev_word->unichar_string(); if (language_model_ngram_space_delimited_language) prev_word_str_ += ' '; } else { - prev_word_str_ += ' '; + prev_word_str_ = " "; } const char *str_ptr = prev_word_str_.string(); const char *str_end = str_ptr + prev_word_str_.length(); @@ -207,227 +163,212 @@ void LanguageModel::InitForWord( } ASSERT_HOST(str_ptr == str_end); } +} - // Initialize blamer-related information: map character boxes recorded in - // blamer_bundle->norm_truth_word to the corresponding i,j indices in the - // ratings matrix. We expect this step to succeed, since when running the - // chopper we checked that the correct chops are present. - if (blamer_bundle != NULL && - blamer_bundle->incorrect_result_reason == IRR_CORRECT && - blamer_bundle->truth_has_char_boxes) { - STRING blamer_debug; - blamer_debug += "Blamer computing correct_segmentation_cols\n"; - int curr_box_col = 0; - int next_box_col = 0; - TBLOB *blob = chunks_record->chunks; - inT16 next_box_x = (blob != NULL) ? blob->bounding_box().right() : 0; - for (int truth_idx = 0; - blob != NULL && truth_idx < blamer_bundle->norm_truth_word.length(); - blob = blob->next) { - ++next_box_col; - inT16 curr_box_x = next_box_x; - if (blob->next != NULL) next_box_x = blob->next->bounding_box().right(); - inT16 truth_x = blamer_bundle->norm_truth_word.BlobBox(truth_idx).right(); - blamer_debug.add_str_int("Box x coord vs. truth: ", curr_box_x); - blamer_debug.add_str_int(" ", truth_x); - blamer_debug += "\n"; - if (curr_box_x > (truth_x + blamer_bundle->norm_box_tolerance)) { - break; // failed to find a matching box - } else if (curr_box_x >= - (truth_x - blamer_bundle->norm_box_tolerance) && // matched - (blob->next == NULL || // next box can't be included - next_box_x > truth_x + blamer_bundle->norm_box_tolerance)) { - blamer_bundle->correct_segmentation_cols.push_back(curr_box_col); - blamer_bundle->correct_segmentation_rows.push_back(next_box_col-1); - ++truth_idx; - blamer_debug.add_str_int("col=", curr_box_col); - blamer_debug.add_str_int(" row=", next_box_col-1); - blamer_debug += "\n"; - curr_box_col = next_box_col; +// Helper scans the collection of predecessors for competing siblings that +// have the same letter with the opposite case, setting competing_vse. +static void ScanParentsForCaseMix(const UNICHARSET& unicharset, + LanguageModelState* parent_node) { + if (parent_node == NULL) return; + ViterbiStateEntry_IT vit(&parent_node->viterbi_state_entries); + for (vit.mark_cycle_pt(); !vit.cycled_list(); vit.forward()) { + ViterbiStateEntry* vse = vit.data(); + vse->competing_vse = NULL; + UNICHAR_ID unichar_id = vse->curr_b->unichar_id(); + if (unicharset.get_isupper(unichar_id) || + unicharset.get_islower(unichar_id)) { + UNICHAR_ID other_case = unicharset.get_other_case(unichar_id); + if (other_case == unichar_id) continue; // Not in unicharset. + // Find other case in same list. There could be multiple entries with + // the same unichar_id, but in theory, they should all point to the + // same BLOB_CHOICE, and that is what we will be using to decide + // which to keep. + ViterbiStateEntry_IT vit2(&parent_node->viterbi_state_entries); + for (vit2.mark_cycle_pt(); !vit2.cycled_list() && + vit2.data()->curr_b->unichar_id() != other_case; + vit2.forward()) {} + if (!vit2.cycled_list()) { + vse->competing_vse = vit2.data(); } } - if (blob != NULL || // trailing blobs - blamer_bundle->correct_segmentation_cols.length() != - blamer_bundle->norm_truth_word.length()) { - blamer_debug.add_str_int("Blamer failed to find correct segmentation" - " (tolerance=", blamer_bundle->norm_box_tolerance); - if (blob == NULL) blamer_debug += " blob == NULL"; - blamer_debug += ")\n"; - blamer_debug.add_str_int( - " path length ", blamer_bundle->correct_segmentation_cols.length()); - blamer_debug.add_str_int(" vs. truth ", - blamer_bundle->norm_truth_word.length()); - blamer_debug += "\n"; - blamer_bundle->SetBlame(IRR_UNKNOWN, blamer_debug, NULL, debug_blamer); - blamer_bundle->correct_segmentation_cols.clear(); - blamer_bundle->correct_segmentation_rows.clear(); - } - } // end if (blamer_bundle != NULL) - - // Start a new hypothesis list for this run of segmentation search. - if (blamer_bundle != NULL) { - blamer_bundle->params_training_bundle.StartHypothesisList(); } } -void LanguageModel::CleanUp() { - for (int i = 0; i < updated_flags_.size(); ++i) { - *(updated_flags_[i]) = false; - } - updated_flags_.clear(); -} - -void LanguageModel::DeleteState(BLOB_CHOICE_LIST *choices) { - BLOB_CHOICE_IT b_it(choices); - for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) { - if (b_it.data()->language_model_state() != NULL) { - LanguageModelState *state = reinterpret_cast( - b_it.data()->language_model_state()); - delete state; - b_it.data()->set_language_model_state(NULL); - } - } +// Helper returns true if the given choice has a better case variant before +// it in the choice_list that is not distinguishable by size. +static bool HasBetterCaseVariant(const UNICHARSET& unicharset, + const BLOB_CHOICE* choice, + BLOB_CHOICE_LIST* choices) { + UNICHAR_ID choice_id = choice->unichar_id(); + UNICHAR_ID other_case = unicharset.get_other_case(choice_id); + if (other_case == choice_id || other_case == INVALID_UNICHAR_ID) + return false; // Not upper or lower or not in unicharset. + if (unicharset.SizesDistinct(choice_id, other_case)) + return false; // Can be separated by size. + BLOB_CHOICE_IT bc_it(choices); + for (bc_it.mark_cycle_pt(); !bc_it.cycled_list(); bc_it.forward()) { + BLOB_CHOICE* better_choice = bc_it.data(); + if (better_choice->unichar_id() == other_case) + return true; // Found an earlier instance of other_case. + else if (better_choice == choice) + return false; // Reached the original choice. + } + return false; // Should never happen, but just in case. } -LanguageModelFlagsType LanguageModel::UpdateState( - LanguageModelFlagsType changed, +// UpdateState has the job of combining the ViterbiStateEntry lists on each +// of the choices on parent_list with each of the blob choices in curr_list, +// making a new ViterbiStateEntry for each sensible path. +// This could be a huge set of combinations, creating a lot of work only to +// be truncated by some beam limit, but only certain kinds of paths will +// continue at the next step: +// paths that are liked by the language model: either a DAWG or the n-gram +// model, where active. +// paths that represent some kind of top choice. The old permuter permuted +// the top raw classifier score, the top upper case word and the top lower- +// case word. UpdateState now concentrates its top-choice paths on top +// lower-case, top upper-case (or caseless alpha), and top digit sequence, +// with allowance for continuation of these paths through blobs where such +// a character does not appear in the choices list. +// GetNextParentVSE enforces some of these models to minimize the number of +// calls to AddViterbiStateEntry, even prior to looking at the language model. +// Thus an n-blob sequence of [l1I] will produce 3n calls to +// AddViterbiStateEntry instead of 3^n. +// Of course it isn't quite that simple as Title Case is handled by allowing +// lower case to continue an upper case initial, but it has to be detected +// in the combiner so it knows which upper case letters are initial alphas. +bool LanguageModel::UpdateState( + bool just_classified, int curr_col, int curr_row, BLOB_CHOICE_LIST *curr_list, - BLOB_CHOICE_LIST *parent_list, - HEAP *pain_points, - BestPathByColumn *best_path_by_column[], - CHUNKS_RECORD *chunks_record, + LanguageModelState *parent_node, + LMPainPoints *pain_points, + WERD_RES *word_res, BestChoiceBundle *best_choice_bundle, BlamerBundle *blamer_bundle) { if (language_model_debug_level > 0) { - tprintf("\nUpdateState: col=%d row=%d (changed=0x%x parent=%p)\n", - curr_col, curr_row, changed, parent_list); + tprintf("\nUpdateState: col=%d row=%d %s", + curr_col, curr_row, just_classified ? "just_classified" : ""); + if (language_model_debug_level > 5) + tprintf("(parent=%p)\n", parent_node); + else + tprintf("\n"); } // Initialize helper variables. - bool word_end = (curr_row+1 >= chunks_record->ratings->dimension()); - bool just_classified = (changed & kJustClassifiedFlag); - LanguageModelFlagsType new_changed = 0x0; + bool word_end = (curr_row+1 >= word_res->ratings->dimension()); + bool new_changed = false; float denom = (language_model_ngram_on) ? ComputeDenom(curr_list) : 1.0f; + const UNICHARSET& unicharset = dict_->getUnicharset(); + BLOB_CHOICE *first_lower = NULL; + BLOB_CHOICE *first_upper = NULL; + BLOB_CHOICE *first_digit = NULL; + bool has_alnum_mix = false; + if (parent_node != NULL) { + int result = SetTopParentLowerUpperDigit(parent_node); + if (result < 0) { + if (language_model_debug_level > 0) + tprintf("No parents found to process\n"); + return false; + } + if (result > 0) + has_alnum_mix = true; + } + if (!GetTopLowerUpperDigit(curr_list, &first_lower, &first_upper, + &first_digit)) + has_alnum_mix = false;; + ScanParentsForCaseMix(unicharset, parent_node); + if (language_model_debug_level > 3 && parent_node != NULL) { + parent_node->Print("Parent viterbi list"); + } + LanguageModelState *curr_state = best_choice_bundle->beam[curr_row]; // Call AddViterbiStateEntry() for each parent+child ViterbiStateEntry. ViterbiStateEntry_IT vit; BLOB_CHOICE_IT c_it(curr_list); - int c_it_counter = 0; - bool first_iteration = true; - BLOB_CHOICE *first_lower = NULL; - BLOB_CHOICE *first_upper = NULL; - GetTopChoiceLowerUpper(changed, curr_list, &first_lower, &first_upper); for (c_it.mark_cycle_pt(); !c_it.cycled_list(); c_it.forward()) { - if (dict_->GetMaxFixedLengthDawgIndex() >= 0 && - c_it_counter++ >= language_model_fixed_length_choices_depth) { - break; - } + BLOB_CHOICE* choice = c_it.data(); + // TODO(antonova): make sure commenting this out if ok for ngram + // model scoring (I think this was introduced to fix ngram model quirks). // Skip NULL unichars unless it is the only choice. - if (!curr_list->singleton() && c_it.data()->unichar_id() == 0) continue; - if (dict_->getUnicharset().get_fragment(c_it.data()->unichar_id())) { + //if (!curr_list->singleton() && c_it.data()->unichar_id() == 0) continue; + UNICHAR_ID unichar_id = choice->unichar_id(); + if (unicharset.get_fragment(unichar_id)) { continue; // skip fragments } // Set top choice flags. - LanguageModelFlagsType top_choice_flags = 0x0; - if (first_iteration && (changed | kSmallestRatingFlag)) { - top_choice_flags |= kSmallestRatingFlag; - } - if (first_lower == c_it.data()) top_choice_flags |= kLowerCaseFlag; - if (first_upper == c_it.data()) top_choice_flags |= kUpperCaseFlag; - - if (parent_list == NULL) { // process the beginning of a word + LanguageModelFlagsType blob_choice_flags = kXhtConsistentFlag; + if (c_it.at_first() || !new_changed) + blob_choice_flags |= kSmallestRatingFlag; + if (first_lower == choice) blob_choice_flags |= kLowerCaseFlag; + if (first_upper == choice) blob_choice_flags |= kUpperCaseFlag; + if (first_digit == choice) blob_choice_flags |= kDigitFlag; + + if (parent_node == NULL) { + // Process the beginning of a word. + // If there is a better case variant that is not distinguished by size, + // skip this blob choice, as we have no choice but to accept the result + // of the character classifier to distinguish between them, even if + // followed by an upper case. + // With words like iPoc, and other CamelBackWords, the lower-upper + // transition can only be achieved if the classifier has the correct case + // as the top choice, and leaving an initial I lower down the list + // increases the chances of choosing IPoc simply because it doesn't + // include such a transition. iPoc will beat iPOC and ipoc because + // the other words are baseline/x-height inconsistent. + if (HasBetterCaseVariant(unicharset, choice, curr_list)) + continue; + // Upper counts as lower at the beginning of a word. + if (blob_choice_flags & kUpperCaseFlag) + blob_choice_flags |= kLowerCaseFlag; new_changed |= AddViterbiStateEntry( - top_choice_flags, denom, word_end, curr_col, curr_row, - c_it.data(), NULL, NULL, pain_points, best_path_by_column, - chunks_record, best_choice_bundle, blamer_bundle); - } else { // get viterbi entries from each of the parent BLOB_CHOICEs - BLOB_CHOICE_IT p_it(parent_list); - for (p_it.mark_cycle_pt(); !p_it.cycled_list(); p_it.forward()) { - LanguageModelState *parent_lms = - reinterpret_cast( - p_it.data()->language_model_state()); - if (parent_lms == NULL || parent_lms->viterbi_state_entries.empty()) { + blob_choice_flags, denom, word_end, curr_col, curr_row, + choice, curr_state, NULL, pain_points, + word_res, best_choice_bundle, blamer_bundle); + } else { + // Get viterbi entries from each parent ViterbiStateEntry. + vit.set_to_list(&parent_node->viterbi_state_entries); + int vit_counter = 0; + vit.mark_cycle_pt(); + ViterbiStateEntry* parent_vse = NULL; + LanguageModelFlagsType top_choice_flags; + while ((parent_vse = GetNextParentVSE(just_classified, has_alnum_mix, + c_it.data(), blob_choice_flags, + unicharset, word_res, &vit, + &top_choice_flags)) != NULL) { + // Skip pruned entries and do not look at prunable entries if already + // examined language_model_viterbi_list_max_num_prunable of those. + if (PrunablePath(*parent_vse) && + (++vit_counter > language_model_viterbi_list_max_num_prunable || + (language_model_ngram_on && parent_vse->ngram_info->pruned))) { continue; } - vit.set_to_list(&(parent_lms->viterbi_state_entries)); - int vit_counter = 0; - for (vit.mark_cycle_pt(); !vit.cycled_list(); vit.forward()) { - // Skip pruned entries and do not look at prunable entries if already - // examined language_model_viterbi_list_max_num_prunable of those. - if (PrunablePath(vit.data()->top_choice_flags, - vit.data()->dawg_info) && - (++vit_counter > language_model_viterbi_list_max_num_prunable || - (language_model_ngram_on && vit.data()->ngram_info->pruned))) { - continue; - } - // Only consider the parent if it has been updated or - // if the current ratings cell has just been classified. - if (!just_classified && !vit.data()->updated) continue; - // Create a new ViterbiStateEntry if BLOB_CHOICE in c_it.data() - // looks good according to the Dawgs or character ngram model. - new_changed |= AddViterbiStateEntry( - top_choice_flags, denom, word_end, curr_col, curr_row, - c_it.data(), p_it.data(), vit.data(), pain_points, - best_path_by_column, chunks_record, - best_choice_bundle, blamer_bundle); - } - } // done looking at parents for this c_it.data() + // If the parent has no alnum choice, (ie choice is the first in a + // string of alnum), and there is a better case variant that is not + // distinguished by size, skip this blob choice/parent, as with the + // initial blob treatment above. + if (!parent_vse->HasAlnumChoice(unicharset) && + HasBetterCaseVariant(unicharset, choice, curr_list)) + continue; + // Create a new ViterbiStateEntry if BLOB_CHOICE in c_it.data() + // looks good according to the Dawgs or character ngram model. + new_changed |= AddViterbiStateEntry( + top_choice_flags, denom, word_end, curr_col, curr_row, + c_it.data(), curr_state, parent_vse, pain_points, + word_res, best_choice_bundle, blamer_bundle); + } } - first_iteration = false; } return new_changed; } -bool LanguageModel::ProblematicPath(const ViterbiStateEntry &vse, - UNICHAR_ID unichar_id, bool word_end) { - // The path is problematic if it is inconsistent and has a parent that - // is consistent (or a NULL parent). - if (!vse.Consistent() && (vse.parent_vse == NULL || - vse.parent_vse->Consistent())) { - if (language_model_debug_level > 0) { - tprintf("ProblematicPath: inconsistent\n"); - } - return true; - } - // The path is problematic if it does not represent a dictionary word, - // while its parent does. - if (vse.dawg_info == NULL && - (vse.parent_vse == NULL || vse.parent_vse->dawg_info != NULL)) { - if (language_model_debug_level > 0) { - tprintf("ProblematicPath: dict word terminated\n"); - } - return true; - } - // The path is problematic if ngram info indicates that this path is - // an unlikely sequence of character, while its parent is does not have - // extreme dips in ngram probabilities. - if (vse.ngram_info != NULL && vse.ngram_info->pruned && - (vse.parent_vse == NULL || !vse.parent_vse->ngram_info->pruned)) { - if (language_model_debug_level > 0) { - tprintf("ProblematicPath: small ngram prob\n"); - } - return true; - } - // The path is problematic if there is a non-alpha character in the - // middle of the path (unless it is a digit in the middle of a path - // that represents a number). - if ((vse.parent_vse != NULL) && !word_end && // is middle - !(dict_->getUnicharset().get_isalpha(unichar_id) || // alpha - (dict_->getUnicharset().get_isdigit(unichar_id) && // ok digit - vse.dawg_info != NULL && vse.dawg_info->permuter == NUMBER_PERM))) { - if (language_model_debug_level > 0) { - tprintf("ProblematicPath: non-alpha middle\n"); - } - return true; - } - return false; -} - -void LanguageModel::GetTopChoiceLowerUpper(LanguageModelFlagsType changed, - BLOB_CHOICE_LIST *curr_list, - BLOB_CHOICE **first_lower, - BLOB_CHOICE **first_upper) { - if (!(changed & kLowerCaseFlag || changed & kUpperCaseFlag)) return; +// Finds the first lower and upper case letter and first digit in curr_list. +// For non-upper/lower languages, alpha counts as upper. +// Uses the first character in the list in place of empty results. +// Returns true if both alpha and digits are found. +bool LanguageModel::GetTopLowerUpperDigit(BLOB_CHOICE_LIST *curr_list, + BLOB_CHOICE **first_lower, + BLOB_CHOICE **first_upper, + BLOB_CHOICE **first_digit) const { BLOB_CHOICE_IT c_it(curr_list); const UNICHARSET &unicharset = dict_->getUnicharset(); BLOB_CHOICE *first_unichar = NULL; @@ -438,190 +379,354 @@ void LanguageModel::GetTopChoiceLowerUpper(LanguageModelFlagsType changed, if (*first_lower == NULL && unicharset.get_islower(unichar_id)) { *first_lower = c_it.data(); } - if (*first_upper == NULL && unicharset.get_isupper(unichar_id)) { + if (*first_upper == NULL && unicharset.get_isalpha(unichar_id) && + !unicharset.get_islower(unichar_id)) { *first_upper = c_it.data(); } + if (*first_digit == NULL && unicharset.get_isdigit(unichar_id)) { + *first_digit = c_it.data(); + } } ASSERT_HOST(first_unichar != NULL); + bool mixed = (*first_lower != NULL || *first_upper != NULL) && + *first_digit != NULL; if (*first_lower == NULL) *first_lower = first_unichar; if (*first_upper == NULL) *first_upper = first_unichar; + if (*first_digit == NULL) *first_digit = first_unichar; + return mixed; +} + +// Forces there to be at least one entry in the overall set of the +// viterbi_state_entries of each element of parent_node that has the +// top_choice_flag set for lower, upper and digit using the same rules as +// GetTopLowerUpperDigit, setting the flag on the first found suitable +// candidate, whether or not the flag is set on some other parent. +// Returns 1 if both alpha and digits are found among the parents, -1 if no +// parents are found at all (a legitimate case), and 0 otherwise. +int LanguageModel::SetTopParentLowerUpperDigit( + LanguageModelState *parent_node) const { + if (parent_node == NULL) return -1; + UNICHAR_ID top_id = INVALID_UNICHAR_ID; + ViterbiStateEntry* top_lower = NULL; + ViterbiStateEntry* top_upper = NULL; + ViterbiStateEntry* top_digit = NULL; + ViterbiStateEntry* top_choice = NULL; + float lower_rating = 0.0f; + float upper_rating = 0.0f; + float digit_rating = 0.0f; + float top_rating = 0.0f; + const UNICHARSET &unicharset = dict_->getUnicharset(); + ViterbiStateEntry_IT vit(&parent_node->viterbi_state_entries); + for (vit.mark_cycle_pt(); !vit.cycled_list(); vit.forward()) { + ViterbiStateEntry* vse = vit.data(); + // INVALID_UNICHAR_ID should be treated like a zero-width joiner, so scan + // back to the real character if needed. + ViterbiStateEntry* unichar_vse = vse; + UNICHAR_ID unichar_id = unichar_vse->curr_b->unichar_id(); + float rating = unichar_vse->curr_b->rating(); + while (unichar_id == INVALID_UNICHAR_ID && + unichar_vse->parent_vse != NULL) { + unichar_vse = unichar_vse->parent_vse; + unichar_id = unichar_vse->curr_b->unichar_id(); + rating = unichar_vse->curr_b->rating(); + } + if (unichar_id != INVALID_UNICHAR_ID) { + if (unicharset.get_islower(unichar_id)) { + if (top_lower == NULL || lower_rating > rating) { + top_lower = vse; + lower_rating = rating; + } + } else if (unicharset.get_isalpha(unichar_id)) { + if (top_upper == NULL || upper_rating > rating) { + top_upper = vse; + upper_rating = rating; + } + } else if (unicharset.get_isdigit(unichar_id)) { + if (top_digit == NULL || digit_rating > rating) { + top_digit = vse; + digit_rating = rating; + } + } + } + if (top_choice == NULL || top_rating > rating) { + top_choice = vse; + top_rating = rating; + top_id = unichar_id; + } + } + if (top_choice == NULL) return -1; + bool mixed = (top_lower != NULL || top_upper != NULL) && + top_digit != NULL; + if (top_lower == NULL) top_lower = top_choice; + top_lower->top_choice_flags |= kLowerCaseFlag; + if (top_upper == NULL) top_upper = top_choice; + top_upper->top_choice_flags |= kUpperCaseFlag; + if (top_digit == NULL) top_digit = top_choice; + top_digit->top_choice_flags |= kDigitFlag; + top_choice->top_choice_flags |= kSmallestRatingFlag; + if (top_id != INVALID_UNICHAR_ID && dict_->compound_marker(top_id) && + (top_choice->top_choice_flags & + (kLowerCaseFlag | kUpperCaseFlag | kDigitFlag))) { + // If the compound marker top choice carries any of the top alnum flags, + // then give it all of them, allowing words like I-295 to be chosen. + top_choice->top_choice_flags |= + kLowerCaseFlag | kUpperCaseFlag | kDigitFlag; + } + return mixed ? 1 : 0; +} + +// Finds the next ViterbiStateEntry with which the given unichar_id can +// combine sensibly, taking into account any mixed alnum/mixed case +// situation, and whether this combination has been inspected before. +ViterbiStateEntry* LanguageModel::GetNextParentVSE( + bool just_classified, bool mixed_alnum, const BLOB_CHOICE* bc, + LanguageModelFlagsType blob_choice_flags, const UNICHARSET& unicharset, + WERD_RES* word_res, ViterbiStateEntry_IT* vse_it, + LanguageModelFlagsType* top_choice_flags) const { + for (; !vse_it->cycled_list(); vse_it->forward()) { + ViterbiStateEntry* parent_vse = vse_it->data(); + // Only consider the parent if it has been updated or + // if the current ratings cell has just been classified. + if (!just_classified && !parent_vse->updated) continue; + if (language_model_debug_level > 2) + parent_vse->Print("Considering"); + // If the parent is non-alnum, then upper counts as lower. + *top_choice_flags = blob_choice_flags; + if ((blob_choice_flags & kUpperCaseFlag) && + !parent_vse->HasAlnumChoice(unicharset)) { + *top_choice_flags |= kLowerCaseFlag; + } + *top_choice_flags &= parent_vse->top_choice_flags; + UNICHAR_ID unichar_id = bc->unichar_id(); + const BLOB_CHOICE* parent_b = parent_vse->curr_b; + UNICHAR_ID parent_id = parent_b->unichar_id(); + // Digits do not bind to alphas if there is a mix in both parent and current + // or if the alpha is not the top choice. + if (unicharset.get_isdigit(unichar_id) && + unicharset.get_isalpha(parent_id) && + (mixed_alnum || *top_choice_flags == 0)) + continue; // Digits don't bind to alphas. + // Likewise alphas do not bind to digits if there is a mix in both or if + // the digit is not the top choice. + if (unicharset.get_isalpha(unichar_id) && + unicharset.get_isdigit(parent_id) && + (mixed_alnum || *top_choice_flags == 0)) + continue; // Alphas don't bind to digits. + // If there is a case mix of the same alpha in the parent list, then + // competing_vse is non-null and will be used to determine whether + // or not to bind the current blob choice. + if (parent_vse->competing_vse != NULL) { + const BLOB_CHOICE* competing_b = parent_vse->competing_vse->curr_b; + UNICHAR_ID other_id = competing_b->unichar_id(); + if (language_model_debug_level >= 5) { + tprintf("Parent %s has competition %s\n", + unicharset.id_to_unichar(parent_id), + unicharset.id_to_unichar(other_id)); + } + if (unicharset.SizesDistinct(parent_id, other_id)) { + // If other_id matches bc wrt position and size, and parent_id, doesn't, + // don't bind to the current parent. + if (bc->PosAndSizeAgree(*competing_b, word_res->x_height, + language_model_debug_level >= 5) && + !bc->PosAndSizeAgree(*parent_b, word_res->x_height, + language_model_debug_level >= 5)) + continue; // Competing blobchoice has a better vertical match. + } + } + vse_it->forward(); + return parent_vse; // This one is good! + } + return NULL; // Ran out of possibilities. } -LanguageModelFlagsType LanguageModel::AddViterbiStateEntry( +bool LanguageModel::AddViterbiStateEntry( LanguageModelFlagsType top_choice_flags, float denom, bool word_end, int curr_col, int curr_row, BLOB_CHOICE *b, - BLOB_CHOICE *parent_b, + LanguageModelState *curr_state, ViterbiStateEntry *parent_vse, - HEAP *pain_points, - BestPathByColumn *best_path_by_column[], - CHUNKS_RECORD *chunks_record, + LMPainPoints *pain_points, + WERD_RES *word_res, BestChoiceBundle *best_choice_bundle, BlamerBundle *blamer_bundle) { ViterbiStateEntry_IT vit; - if (language_model_debug_level > 0) { - tprintf("\nAddViterbiStateEntry for unichar %s rating=%.4f" - " certainty=%.4f top_choice_flags=0x%x parent_vse=%p\n", + if (language_model_debug_level > 1) { + tprintf("AddViterbiStateEntry for unichar %s rating=%.4f" + " certainty=%.4f top_choice_flags=0x%x", dict_->getUnicharset().id_to_unichar(b->unichar_id()), - b->rating(), b->certainty(), top_choice_flags, parent_vse); - if (language_model_debug_level > 3 && b->language_model_state() != NULL) { - tprintf("Existing viterbi list:\n"); - vit.set_to_list(&(reinterpret_cast( - b->language_model_state())->viterbi_state_entries)); - for (vit.mark_cycle_pt(); !vit.cycled_list(); vit.forward()) { - PrintViterbiStateEntry("", vit.data(), b, chunks_record); - } - } + b->rating(), b->certainty(), top_choice_flags); + if (language_model_debug_level > 5) + tprintf(" parent_vse=%p\n", parent_vse); + else + tprintf("\n"); } // Check whether the list is full. - if (b->language_model_state() != NULL && - (reinterpret_cast( - b->language_model_state())->viterbi_state_entries_length >= - language_model_viterbi_list_max_size)) { + if (curr_state != NULL && + curr_state->viterbi_state_entries_length >= + language_model_viterbi_list_max_size) { if (language_model_debug_level > 1) { tprintf("AddViterbiStateEntry: viterbi list is full!\n"); } - return 0x0; - } - - LanguageModelFlagsType changed = 0x0; - float optimistic_cost = 0.0f; - if (!language_model_ngram_on) optimistic_cost += b->rating(); - if (parent_vse != NULL) optimistic_cost += parent_vse->cost; - // Discard this entry if it will not beat best choice. - if (optimistic_cost >= best_choice_bundle->best_choice->rating()) { - if (language_model_debug_level > 1) { - tprintf("Discarded ViterbiEntry with high cost %.4f" - " best_choice->rating()=%.4f\n", optimistic_cost, - best_choice_bundle->best_choice->rating()); - } - return 0x0; + return false; } - // Check consistency of the path and set the relevant consistency_info. - LanguageModelConsistencyInfo consistency_info; - FillConsistencyInfo(curr_col, word_end, b, parent_vse, parent_b, - chunks_record, &consistency_info); - // Invoke Dawg language model component. LanguageModelDawgInfo *dawg_info = - GenerateDawgInfo(word_end, consistency_info.script_id, - curr_col, curr_row, *b, parent_vse, &changed); - - // Invoke TopChoice language model component - float ratings_sum = b->rating(); - if (parent_vse != NULL) ratings_sum += parent_vse->ratings_sum; - GenerateTopChoiceInfo(ratings_sum, dawg_info, consistency_info, - parent_vse, b, &top_choice_flags, &changed); + GenerateDawgInfo(word_end, curr_col, curr_row, *b, parent_vse); + float outline_length = + AssociateUtils::ComputeOutlineLength(rating_cert_scale_, *b); // Invoke Ngram language model component. LanguageModelNgramInfo *ngram_info = NULL; if (language_model_ngram_on) { ngram_info = GenerateNgramInfo( dict_->getUnicharset().id_to_unichar(b->unichar_id()), b->certainty(), - denom, curr_col, curr_row, parent_vse, parent_b, &changed); + denom, curr_col, curr_row, outline_length, parent_vse); ASSERT_HOST(ngram_info != NULL); } - - // Prune non-top choice paths with inconsistent scripts. - if (consistency_info.inconsistent_script) { - if (!(top_choice_flags & kSmallestRatingFlag)) changed = 0x0; - if (ngram_info != NULL) ngram_info->pruned = true; + bool liked_by_language_model = dawg_info != NULL || + (ngram_info != NULL && !ngram_info->pruned); + // Quick escape if not liked by the language model, can't be consistent + // xheight, and not top choice. + if (!liked_by_language_model && top_choice_flags == 0) { + if (language_model_debug_level > 1) { + tprintf("Language model components very early pruned this entry\n"); + } + delete ngram_info; + return false; } - // If language model components did not like this unichar - return - if (!changed) { - if (language_model_debug_level > 0) { - tprintf("Language model components did not like this entry\n"); + // Check consistency of the path and set the relevant consistency_info. + LMConsistencyInfo consistency_info( + parent_vse != NULL ? &parent_vse->consistency_info : NULL); + // Start with just the x-height consistency, as it provides significant + // pruning opportunity. + consistency_info.ComputeXheightConsistency( + b, dict_->getUnicharset().get_ispunctuation(b->unichar_id())); + // Turn off xheight consistent flag if not consistent. + if (consistency_info.InconsistentXHeight()) { + top_choice_flags &= ~kXhtConsistentFlag; + } + + // Quick escape if not liked by the language model, not consistent xheight, + // and not top choice. + if (!liked_by_language_model && top_choice_flags == 0) { + if (language_model_debug_level > 1) { + tprintf("Language model components early pruned this entry\n"); } - delete dawg_info; delete ngram_info; - return 0x0; + return false; + } + + // Compute the rest of the consistency info. + FillConsistencyInfo(curr_col, word_end, b, parent_vse, + word_res, &consistency_info); + if (dawg_info != NULL && consistency_info.invalid_punc) { + consistency_info.invalid_punc = false; // do not penalize dict words } // Compute cost of associating the blobs that represent the current unichar. AssociateStats associate_stats; ComputeAssociateStats(curr_col, curr_row, max_char_wh_ratio_, - parent_vse, chunks_record, &associate_stats); + parent_vse, word_res, &associate_stats); if (parent_vse != NULL) { associate_stats.shape_cost += parent_vse->associate_stats.shape_cost; associate_stats.bad_shape |= parent_vse->associate_stats.bad_shape; } - // Compute the aggregate cost (adjusted ratings sum). - float cost = ComputeAdjustedPathCost( - ratings_sum, - (parent_vse == NULL) ? 1 : (parent_vse->length+1), - (dawg_info == NULL) ? 0.0f : 1.0f, - dawg_info, ngram_info, consistency_info, associate_stats, parent_vse); + // Create the new ViterbiStateEntry compute the adjusted cost of the path. + ViterbiStateEntry *new_vse = new ViterbiStateEntry( + parent_vse, b, 0.0, outline_length, + consistency_info, associate_stats, top_choice_flags, dawg_info, + ngram_info, (language_model_debug_level > 0) ? + dict_->getUnicharset().id_to_unichar(b->unichar_id()) : NULL); + new_vse->cost = ComputeAdjustedPathCost(new_vse); - if (b->language_model_state() == NULL) { - b->set_language_model_state(new LanguageModelState(curr_col, curr_row)); + // Invoke Top Choice language model component to make the final adjustments + // to new_vse->top_choice_flags. + if (!curr_state->viterbi_state_entries.empty() && new_vse->top_choice_flags) { + GenerateTopChoiceInfo(new_vse, parent_vse, curr_state); + } + + // If language model components did not like this unichar - return. + bool keep = new_vse->top_choice_flags || liked_by_language_model; + if (!(top_choice_flags & kSmallestRatingFlag) && // no non-top choice paths + consistency_info.inconsistent_script) { // with inconsistent script + keep = false; + } + if (!keep) { + if (language_model_debug_level > 1) { + tprintf("Language model components did not like this entry\n"); + } + delete new_vse; + return false; } - LanguageModelState *lms = - reinterpret_cast(b->language_model_state()); // Discard this entry if it represents a prunable path and // language_model_viterbi_list_max_num_prunable such entries with a lower // cost have already been recorded. - if (PrunablePath(top_choice_flags, dawg_info) && - (lms->viterbi_state_entries_prunable_length >= + if (PrunablePath(*new_vse) && + (curr_state->viterbi_state_entries_prunable_length >= language_model_viterbi_list_max_num_prunable) && - cost >= lms->viterbi_state_entries_prunable_max_cost) { + new_vse->cost >= curr_state->viterbi_state_entries_prunable_max_cost) { if (language_model_debug_level > 1) { tprintf("Discarded ViterbiEntry with high cost %g max cost %g\n", - cost, lms->viterbi_state_entries_prunable_max_cost); + new_vse->cost, + curr_state->viterbi_state_entries_prunable_max_cost); } - delete dawg_info; - delete ngram_info; - return 0x0; + delete new_vse; + return false; } - // Create the new ViterbiStateEntry and add it to lms->viterbi_state_entries - ViterbiStateEntry *new_vse = new ViterbiStateEntry( - parent_b, parent_vse, b, cost, ComputeOutlineLength(b), consistency_info, - associate_stats, top_choice_flags, dawg_info, ngram_info); - updated_flags_.push_back(&(new_vse->updated)); - lms->viterbi_state_entries.add_sorted(ViterbiStateEntry::Compare, - false, new_vse); - lms->viterbi_state_entries_length++; - if (PrunablePath(top_choice_flags, dawg_info)) { - lms->viterbi_state_entries_prunable_length++; + // Update best choice if needed. + if (word_end) { + UpdateBestChoice(new_vse, pain_points, word_res, + best_choice_bundle, blamer_bundle); + // Discard the entry if UpdateBestChoice() found flaws in it. + if (new_vse->cost >= WERD_CHOICE::kBadRating && + new_vse != best_choice_bundle->best_vse) { + if (language_model_debug_level > 1) { + tprintf("Discarded ViterbiEntry with high cost %g\n", new_vse->cost); + } + delete new_vse; + return false; + } + } + + // Add the new ViterbiStateEntry and to curr_state->viterbi_state_entries. + curr_state->viterbi_state_entries.add_sorted(ViterbiStateEntry::Compare, + false, new_vse); + curr_state->viterbi_state_entries_length++; + if (PrunablePath(*new_vse)) { + curr_state->viterbi_state_entries_prunable_length++; } // Update lms->viterbi_state_entries_prunable_max_cost and clear // top_choice_flags of entries with ratings_sum than new_vse->ratings_sum. - if ((lms->viterbi_state_entries_prunable_length >= - language_model_viterbi_list_max_num_prunable) || top_choice_flags) { - ASSERT_HOST(!lms->viterbi_state_entries.empty()); + if ((curr_state->viterbi_state_entries_prunable_length >= + language_model_viterbi_list_max_num_prunable) || + new_vse->top_choice_flags) { + ASSERT_HOST(!curr_state->viterbi_state_entries.empty()); int prunable_counter = language_model_viterbi_list_max_num_prunable; - vit.set_to_list(&(lms->viterbi_state_entries)); + vit.set_to_list(&(curr_state->viterbi_state_entries)); for (vit.mark_cycle_pt(); !vit.cycled_list(); vit.forward()) { ViterbiStateEntry *curr_vse = vit.data(); // Clear the appropriate top choice flags of the entries in the - // list that have ratings_sum higher thank new_entry->ratings_sum + // list that have cost higher thank new_entry->cost // (since they will not be top choices any more). if (curr_vse->top_choice_flags && curr_vse != new_vse && - ComputeConsistencyAdjustedRatingsSum( - curr_vse->ratings_sum, curr_vse->dawg_info, - curr_vse->consistency_info) > - ComputeConsistencyAdjustedRatingsSum( - new_vse->ratings_sum, new_vse->dawg_info, - new_vse->consistency_info)) { - curr_vse->top_choice_flags &= ~(top_choice_flags); - } - if (prunable_counter > 0 && - PrunablePath(curr_vse->top_choice_flags, curr_vse->dawg_info)) { - --prunable_counter; + curr_vse->cost > new_vse->cost) { + curr_vse->top_choice_flags &= ~(new_vse->top_choice_flags); } - // Update lms->viterbi_state_entries_prunable_max_cost. + if (prunable_counter > 0 && PrunablePath(*curr_vse)) --prunable_counter; + // Update curr_state->viterbi_state_entries_prunable_max_cost. if (prunable_counter == 0) { - lms->viterbi_state_entries_prunable_max_cost = vit.data()->cost; + curr_state->viterbi_state_entries_prunable_max_cost = vit.data()->cost; if (language_model_debug_level > 1) { - tprintf("Set viterbi_state_entries_prunable_max_cost to %.4f\n", - lms->viterbi_state_entries_prunable_max_cost); + tprintf("Set viterbi_state_entries_prunable_max_cost to %g\n", + curr_state->viterbi_state_entries_prunable_max_cost); } prunable_counter = -1; // stop counting } @@ -630,149 +735,57 @@ LanguageModelFlagsType LanguageModel::AddViterbiStateEntry( // Print the newly created ViterbiStateEntry. if (language_model_debug_level > 2) { - PrintViterbiStateEntry("New", new_vse, b, chunks_record); - if (language_model_debug_level > 3) { - tprintf("Updated viterbi list (max cost %g):\n", - lms->viterbi_state_entries_prunable_max_cost); - vit.set_to_list(&(lms->viterbi_state_entries)); - for (vit.mark_cycle_pt(); !vit.cycled_list(); vit.forward()) { - PrintViterbiStateEntry("", vit.data(), b, chunks_record); - } - } - } - - // Update best choice if needed. - if (word_end) { - UpdateBestChoice(b, new_vse, pain_points, chunks_record, - best_choice_bundle, blamer_bundle); - } - - // Update stats in best_path_by_column. - if (new_vse->Consistent() || new_vse->dawg_info != NULL || - (new_vse->ngram_info != NULL && !new_vse->ngram_info->pruned)) { - float avg_cost = new_vse->cost / static_cast(curr_row+1); - for (int c = curr_col; c <= curr_row; ++c) { - if (avg_cost < (*best_path_by_column)[c].avg_cost) { - (*best_path_by_column)[c].avg_cost = avg_cost; - (*best_path_by_column)[c].best_vse = new_vse; - (*best_path_by_column)[c].best_b = b; - if (language_model_debug_level > 0) { - tprintf("Set best_path_by_column[%d]=(%g %p)\n", - c, avg_cost, new_vse); - } - } - } + new_vse->Print("New"); + if (language_model_debug_level > 5) + curr_state->Print("Updated viterbi list"); } - return changed; -} -void LanguageModel::PrintViterbiStateEntry( - const char *msg, ViterbiStateEntry *vse, - BLOB_CHOICE *b, CHUNKS_RECORD *chunks_record) { - tprintf("%s ViterbiStateEntry %p with ratings_sum=%.4f length=%d cost=%.4f", - msg, vse, vse->ratings_sum, vse->length, vse->cost); - if (vse->top_choice_flags) { - tprintf(" top_choice_flags=0x%x", vse->top_choice_flags); - } - if (!vse->Consistent()) { - tprintf(" inconsistent=(punc %d case %d chartype %d script %d)\n", - vse->consistency_info.NumInconsistentPunc(), - vse->consistency_info.NumInconsistentCase(), - vse->consistency_info.NumInconsistentChartype(), - vse->consistency_info.inconsistent_script); - } - if (vse->dawg_info) tprintf(" permuter=%d", vse->dawg_info->permuter); - if (vse->ngram_info) { - tprintf(" ngram_cost=%g context=%s ngram pruned=%d", - vse->ngram_info->ngram_cost, - vse->ngram_info->context.string(), - vse->ngram_info->pruned); - } - if (vse->associate_stats.shape_cost > 0.0f) { - tprintf(" shape_cost=%g", vse->associate_stats.shape_cost); - } - if (language_model_debug_level > 3) { - STRING wd_str; - WERD_CHOICE *wd = ConstructWord(b, vse, chunks_record, - NULL, NULL, NULL, NULL, NULL, NULL); - wd->string_and_lengths(&wd_str, NULL); - delete wd; - tprintf(" str=%s", wd_str.string()); - } - tprintf("\n"); + return true; } -void LanguageModel::GenerateTopChoiceInfo( - float ratings_sum, - const LanguageModelDawgInfo *dawg_info, - const LanguageModelConsistencyInfo &consistency_info, - const ViterbiStateEntry *parent_vse, - BLOB_CHOICE *b, - LanguageModelFlagsType *top_choice_flags, - LanguageModelFlagsType *changed) { - ratings_sum = ComputeConsistencyAdjustedRatingsSum( - ratings_sum, dawg_info, consistency_info); - // Clear flags that do not agree with parent_vse->top_choice_flags. - if (parent_vse != NULL) *top_choice_flags &= parent_vse->top_choice_flags; - if (consistency_info.Consistent()) *top_choice_flags |= kConsistentFlag; - if (*top_choice_flags == 0x0) return; - LanguageModelState *lms = - reinterpret_cast(b->language_model_state()); - if (lms != NULL && !lms->viterbi_state_entries.empty()) { - ViterbiStateEntry_IT vit(&(lms->viterbi_state_entries)); - for (vit.mark_cycle_pt(); !vit.cycled_list(); vit.forward()) { - if (ratings_sum >= ComputeConsistencyAdjustedRatingsSum( - vit.data()->ratings_sum, vit.data()->dawg_info, - vit.data()->consistency_info)) { - // Clear the appropriate flags if the list already contains - // a top choice entry with a lower cost. - *top_choice_flags &= ~(vit.data()->top_choice_flags); - } - } +void LanguageModel::GenerateTopChoiceInfo(ViterbiStateEntry *new_vse, + const ViterbiStateEntry *parent_vse, + LanguageModelState *lms) { + ViterbiStateEntry_IT vit(&(lms->viterbi_state_entries)); + for (vit.mark_cycle_pt(); !vit.cycled_list() && new_vse->top_choice_flags && + new_vse->cost >= vit.data()->cost; vit.forward()) { + // Clear the appropriate flags if the list already contains + // a top choice entry with a lower cost. + new_vse->top_choice_flags &= ~(vit.data()->top_choice_flags); } - if (language_model_debug_level > 0) { + if (language_model_debug_level > 2) { tprintf("GenerateTopChoiceInfo: top_choice_flags=0x%x\n", - *top_choice_flags); + new_vse->top_choice_flags); } - *changed |= *top_choice_flags; } LanguageModelDawgInfo *LanguageModel::GenerateDawgInfo( - bool word_end, int script_id, + bool word_end, int curr_col, int curr_row, const BLOB_CHOICE &b, - const ViterbiStateEntry *parent_vse, - LanguageModelFlagsType *changed) { - bool use_fixed_length_dawgs = !fixed_length_beginning_active_dawgs_->empty(); - - // Initialize active_dawgs and constraints from parent_vse if it is not NULL, - // otherwise use beginning_active_dawgs_ and beginning_constraints_. - if (parent_vse == NULL || - (use_fixed_length_dawgs && parent_vse->dawg_info == NULL)) { - dawg_args_->active_dawgs = beginning_active_dawgs_; - dawg_args_->constraints = beginning_constraints_; + const ViterbiStateEntry *parent_vse) { + // Initialize active_dawgs from parent_vse if it is not NULL. + // Otherwise use very_beginning_active_dawgs_. + if (parent_vse == NULL) { + dawg_args_->active_dawgs = very_beginning_active_dawgs_; dawg_args_->permuter = NO_PERM; } else { if (parent_vse->dawg_info == NULL) return NULL; // not a dict word path dawg_args_->active_dawgs = parent_vse->dawg_info->active_dawgs; - dawg_args_->constraints = parent_vse->dawg_info->constraints; dawg_args_->permuter = parent_vse->dawg_info->permuter; } // Deal with hyphenated words. - if (!use_fixed_length_dawgs && word_end && - dict_->has_hyphen_end(b.unichar_id(), curr_col == 0)) { + if (word_end && dict_->has_hyphen_end(b.unichar_id(), curr_col == 0)) { if (language_model_debug_level > 0) tprintf("Hyphenated word found\n"); - *changed |= kDawgFlag; return new LanguageModelDawgInfo(dawg_args_->active_dawgs, - dawg_args_->constraints, COMPOUND_PERM); } // Deal with compound words. - if (!use_fixed_length_dawgs && dict_->compound_marker(b.unichar_id()) && + if (dict_->compound_marker(b.unichar_id()) && (parent_vse == NULL || parent_vse->dawg_info->permuter != NUMBER_PERM)) { - if (language_model_debug_level > 0) tprintf("Found compound marker"); + if (language_model_debug_level > 0) tprintf("Found compound marker\n"); // Do not allow compound operators at the beginning and end of the word. // Do not allow more than one compound operator per word. // Do not allow compounding of words with lengths shorter than @@ -785,68 +798,62 @@ LanguageModelDawgInfo *LanguageModel::GenerateDawgInfo( // Check a that the path terminated before the current character is a word. bool has_word_ending = false; for (i = 0; i < parent_vse->dawg_info->active_dawgs->size(); ++i) { - const DawgInfo &info = (*parent_vse->dawg_info->active_dawgs)[i]; - const Dawg *pdawg = dict_->GetDawg(info.dawg_index); - assert(pdawg != NULL); - if (pdawg->type() == DAWG_TYPE_WORD && info.ref != NO_EDGE && - pdawg->end_of_word(info.ref)) { + const DawgPosition &pos = (*parent_vse->dawg_info->active_dawgs)[i]; + const Dawg *pdawg = pos.dawg_index < 0 + ? NULL : dict_->GetDawg(pos.dawg_index); + if (pdawg == NULL || pos.back_to_punc) continue;; + if (pdawg->type() == DAWG_TYPE_WORD && pos.dawg_ref != NO_EDGE && + pdawg->end_of_word(pos.dawg_ref)) { has_word_ending = true; break; } } if (!has_word_ending) return NULL; - // Return LanguageModelDawgInfo with active_dawgs set to - // the beginning dawgs of type DAWG_TYPE_WORD. if (language_model_debug_level > 0) tprintf("Compound word found\n"); - DawgInfoVector beginning_word_dawgs; - for (i = 0; i < beginning_active_dawgs_->size(); ++i) { - const Dawg *bdawg = - dict_->GetDawg((*beginning_active_dawgs_)[i].dawg_index); - if (bdawg->type() == DAWG_TYPE_WORD) { - beginning_word_dawgs += (*beginning_active_dawgs_)[i]; - } - } - *changed |= kDawgFlag; - return new LanguageModelDawgInfo(&(beginning_word_dawgs), - dawg_args_->constraints, - COMPOUND_PERM); + return new LanguageModelDawgInfo(beginning_active_dawgs_, COMPOUND_PERM); } // done dealing with compound words LanguageModelDawgInfo *dawg_info = NULL; // Call LetterIsOkay(). - dict_->LetterIsOkay(dawg_args_, b.unichar_id(), word_end); + // Use the normalized IDs so that all shapes of ' can be allowed in words + // like don't. + const GenericVector& normed_ids = + dict_->getUnicharset().normed_ids(b.unichar_id()); + DawgPositionVector tmp_active_dawgs; + for (int i = 0; i < normed_ids.size(); ++i) { + if (language_model_debug_level > 2) + tprintf("Test Letter OK for unichar %d, normed %d\n", + b.unichar_id(), normed_ids[i]); + dict_->LetterIsOkay(dawg_args_, normed_ids[i], + word_end && i == normed_ids.size() - 1); + if (dawg_args_->permuter == NO_PERM) { + break; + } else if (i < normed_ids.size() - 1) { + tmp_active_dawgs = *dawg_args_->updated_dawgs; + dawg_args_->active_dawgs = &tmp_active_dawgs; + } + if (language_model_debug_level > 2) + tprintf("Letter was OK for unichar %d, normed %d\n", + b.unichar_id(), normed_ids[i]); + } + dawg_args_->active_dawgs = NULL; if (dawg_args_->permuter != NO_PERM) { - *changed |= kDawgFlag; - dawg_info = new LanguageModelDawgInfo(dawg_args_->updated_active_dawgs, - dawg_args_->updated_constraints, + dawg_info = new LanguageModelDawgInfo(dawg_args_->updated_dawgs, dawg_args_->permuter); + } else if (language_model_debug_level > 3) { + tprintf("Letter %s not OK!\n", + dict_->getUnicharset().id_to_unichar(b.unichar_id())); } - // For non-space delimited languages: since every letter could be - // a valid word, a new word could start at every unichar. Thus append - // fixed_length_beginning_active_dawgs_ to dawg_info->active_dawgs. - if (use_fixed_length_dawgs) { - if (dawg_info == NULL) { - *changed |= kDawgFlag; - dawg_info = new LanguageModelDawgInfo( - fixed_length_beginning_active_dawgs_, - empty_dawg_info_vec_, SYSTEM_DAWG_PERM); - } else { - *(dawg_info->active_dawgs) += *(fixed_length_beginning_active_dawgs_); - } - } // done dealing with fixed-length dawgs - return dawg_info; } LanguageModelNgramInfo *LanguageModel::GenerateNgramInfo( const char *unichar, float certainty, float denom, - int curr_col, int curr_row, - const ViterbiStateEntry *parent_vse, - BLOB_CHOICE *parent_b, - LanguageModelFlagsType *changed) { + int curr_col, int curr_row, float outline_length, + const ViterbiStateEntry *parent_vse) { // Initialize parent context. const char *pcontext_ptr = ""; int pcontext_unichar_step_len = 0; @@ -861,23 +868,21 @@ LanguageModelNgramInfo *LanguageModel::GenerateNgramInfo( // Compute p(unichar | parent context). int unichar_step_len = 0; bool pruned = false; - float ngram_prob; - float ngram_cost = ComputeNgramCost(unichar, certainty, denom, - pcontext_ptr, &unichar_step_len, - &pruned, &ngram_prob); - // First attempt to normalize ngram_cost for strings of different - // lengths - we multiply ngram_cost by P(char | context) as many times - // as the number of chunks occupied by char. This makes the ngram costs - // of all the paths ending at the current BLOB_CHOICE comparable. - // TODO(daria): it would be worth looking at different ways of normalization. - if (curr_row > curr_col) { - ngram_cost += (curr_row - curr_col) * ngram_cost; - ngram_prob += (curr_row - curr_col) * ngram_prob; - } + float ngram_cost; + float ngram_and_classifier_cost = + ComputeNgramCost(unichar, certainty, denom, + pcontext_ptr, &unichar_step_len, + &pruned, &ngram_cost); + // Normalize just the ngram_and_classifier_cost by outline_length. + // The ngram_cost is used by the params_model, so it needs to be left as-is, + // and the params model cost will be normalized by outline_length. + ngram_and_classifier_cost *= + outline_length / language_model_ngram_rating_factor; // Add the ngram_cost of the parent. if (parent_vse != NULL) { + ngram_and_classifier_cost += + parent_vse->ngram_info->ngram_and_classifier_cost; ngram_cost += parent_vse->ngram_info->ngram_cost; - ngram_prob += parent_vse->ngram_info->ngram_prob; } // Shorten parent context string by unichar_step_len unichars. @@ -891,11 +896,11 @@ LanguageModelNgramInfo *LanguageModel::GenerateNgramInfo( // Decide whether to prune this ngram path and update changed accordingly. if (parent_vse != NULL && parent_vse->ngram_info->pruned) pruned = true; - if (!pruned) *changed |= kNgramFlag; // Construct and return the new LanguageModelNgramInfo. LanguageModelNgramInfo *ngram_info = new LanguageModelNgramInfo( - pcontext_ptr, pcontext_unichar_step_len, pruned, ngram_prob, ngram_cost); + pcontext_ptr, pcontext_unichar_step_len, pruned, ngram_cost, + ngram_and_classifier_cost); ngram_info->context += unichar; ngram_info->context_unichar_step_len += unichar_step_len; assert(ngram_info->context_unichar_step_len <= language_model_ngram_order); @@ -908,7 +913,7 @@ float LanguageModel::ComputeNgramCost(const char *unichar, const char *context, int *unichar_step_len, bool *found_small_prob, - float *ngram_prob) { + float *ngram_cost) { const char *context_ptr = context; char *modified_context = NULL; char *modified_context_end = NULL; @@ -947,16 +952,19 @@ float LanguageModel::ComputeNgramCost(const char *unichar, if (prob < language_model_ngram_small_prob) { if (language_model_debug_level > 0) tprintf("Found small prob %g\n", prob); *found_small_prob = true; + prob = language_model_ngram_small_prob; } - *ngram_prob = -1.0*log(prob); - float cost = -1.0*log(CertaintyScore(certainty)/denom) + - *ngram_prob * language_model_ngram_scale_factor; + *ngram_cost = -1.0*log2(prob); + float ngram_and_classifier_cost = + -1.0*log2(CertaintyScore(certainty)/denom) + + *ngram_cost * language_model_ngram_scale_factor; if (language_model_debug_level > 1) { - tprintf("-log [ p(%s) * p(%s | %s) ] = -log(%g*%g) = %g\n", unichar, - unichar, context_ptr, CertaintyScore(certainty)/denom, prob, cost); + tprintf("-log [ p(%s) * p(%s | %s) ] = -log2(%g*%g) = %g\n", unichar, + unichar, context_ptr, CertaintyScore(certainty)/denom, prob, + ngram_and_classifier_cost); } if (modified_context != NULL) delete[] modified_context; - return cost; + return ngram_and_classifier_cost; } float LanguageModel::ComputeDenom(BLOB_CHOICE_LIST *curr_list) { @@ -985,12 +993,11 @@ void LanguageModel::FillConsistencyInfo( bool word_end, BLOB_CHOICE *b, ViterbiStateEntry *parent_vse, - BLOB_CHOICE *parent_b, - CHUNKS_RECORD *chunks_record, - LanguageModelConsistencyInfo *consistency_info) { + WERD_RES *word_res, + LMConsistencyInfo *consistency_info) { const UNICHARSET &unicharset = dict_->getUnicharset(); UNICHAR_ID unichar_id = b->unichar_id(); - if (parent_vse != NULL) *consistency_info = parent_vse->consistency_info; + BLOB_CHOICE* parent_b = parent_vse != NULL ? parent_vse->curr_b : NULL; // Check punctuation validity. if (unicharset.get_ispunctuation(unichar_id)) consistency_info->num_punc++; @@ -1001,9 +1008,14 @@ void LanguageModel::FillConsistencyInfo( // reset punc_ref for compound words consistency_info->punc_ref = NO_EDGE; } else { + bool is_apos = dict_->is_apostrophe(unichar_id); + bool prev_is_numalpha = (parent_b != NULL && + (unicharset.get_isalpha(parent_b->unichar_id()) || + unicharset.get_isdigit(parent_b->unichar_id()))); UNICHAR_ID pattern_unichar_id = (unicharset.get_isalpha(unichar_id) || - unicharset.get_isdigit(unichar_id)) ? + unicharset.get_isdigit(unichar_id) || + (is_apos && prev_is_numalpha)) ? Dawg::kPatternUnicharID : unichar_id; if (consistency_info->punc_ref == NO_EDGE || pattern_unichar_id != Dawg::kPatternUnicharID || @@ -1075,7 +1087,7 @@ void LanguageModel::FillConsistencyInfo( } // Check font and spacing consistency. - if (parent_b != NULL) { + if (fontinfo_table_->size() > 0 && parent_b != NULL) { int fontinfo_id = -1; if (parent_b->fontinfo_id() == b->fontinfo_id() || parent_b->fontinfo_id2() == b->fontinfo_id()) { @@ -1096,328 +1108,271 @@ void LanguageModel::FillConsistencyInfo( (fontinfo_id >= 0) ? fontinfo_table_->get(fontinfo_id).name : "", fontinfo_id); } - bool expected_gap_found = false; - float expected_gap; - int temp_gap; - if (fontinfo_id >= 0) { // found a common font - if (fontinfo_table_->get(fontinfo_id).get_spacing( - parent_b->unichar_id(), unichar_id, &temp_gap)) { - expected_gap = temp_gap; - expected_gap_found = true; - } - } else { - consistency_info->inconsistent_font = true; - // Get an average of the expected gaps in each font - int num_addends = 0; - expected_gap = 0; - int temp_fid; - for (int i = 0; i < 4; ++i) { - if (i == 0) { - temp_fid = parent_b->fontinfo_id(); - } else if (i == 1) { - temp_fid = parent_b->fontinfo_id2(); - } else if (i == 2) { - temp_fid = b->fontinfo_id(); - } else { - temp_fid = b->fontinfo_id2(); - } - if (temp_fid >= 0 && fontinfo_table_->get(temp_fid).get_spacing( + if (!word_res->blob_widths.empty()) { // if we have widths/gaps info + bool expected_gap_found = false; + float expected_gap; + int temp_gap; + if (fontinfo_id >= 0) { // found a common font + ASSERT_HOST(fontinfo_id < fontinfo_table_->size()); + if (fontinfo_table_->get(fontinfo_id).get_spacing( parent_b->unichar_id(), unichar_id, &temp_gap)) { - expected_gap += temp_gap; - num_addends++; + expected_gap = temp_gap; + expected_gap_found = true; + } + } else { + consistency_info->inconsistent_font = true; + // Get an average of the expected gaps in each font + int num_addends = 0; + expected_gap = 0; + int temp_fid; + for (int i = 0; i < 4; ++i) { + if (i == 0) { + temp_fid = parent_b->fontinfo_id(); + } else if (i == 1) { + temp_fid = parent_b->fontinfo_id2(); + } else if (i == 2) { + temp_fid = b->fontinfo_id(); + } else { + temp_fid = b->fontinfo_id2(); + } + ASSERT_HOST(temp_fid < 0 || fontinfo_table_->size()); + if (temp_fid >= 0 && fontinfo_table_->get(temp_fid).get_spacing( + parent_b->unichar_id(), unichar_id, &temp_gap)) { + expected_gap += temp_gap; + num_addends++; + } + } + expected_gap_found = (num_addends > 0); + if (num_addends > 0) { + expected_gap /= static_cast(num_addends); } } - expected_gap_found = (num_addends > 0); - if (num_addends > 0) expected_gap /= static_cast(num_addends); - } - if (expected_gap_found) { - float actual_gap = - static_cast(AssociateUtils::GetChunksGap( - chunks_record->chunk_widths, curr_col-1)); - float gap_ratio = expected_gap / actual_gap; - // TODO(daria): find a good way to tune this heuristic estimate. - if (gap_ratio < 1/2 || gap_ratio > 2) { - consistency_info->num_inconsistent_spaces++; - } - if(language_model_debug_level > 1) { - tprintf("spacing for %s(%d) %s(%d) col %d: expected %g actual %g\n", - unicharset.id_to_unichar(parent_b->unichar_id()), - parent_b->unichar_id(), unicharset.id_to_unichar(unichar_id), - unichar_id, curr_col, expected_gap, actual_gap); + if (expected_gap_found) { + float actual_gap = + static_cast(word_res->GetBlobsGap(curr_col-1)); + float gap_ratio = expected_gap / actual_gap; + // TODO(daria): find a good way to tune this heuristic estimate. + if (gap_ratio < 1/2 || gap_ratio > 2) { + consistency_info->num_inconsistent_spaces++; + } + if (language_model_debug_level > 1) { + tprintf("spacing for %s(%d) %s(%d) col %d: expected %g actual %g\n", + unicharset.id_to_unichar(parent_b->unichar_id()), + parent_b->unichar_id(), unicharset.id_to_unichar(unichar_id), + unichar_id, curr_col, expected_gap, actual_gap); + } } } } } -float LanguageModel::ComputeAdjustedPathCost( - float ratings_sum, int length, float dawg_score, - const LanguageModelDawgInfo *dawg_info, - const LanguageModelNgramInfo *ngram_info, - const LanguageModelConsistencyInfo &consistency_info, - const AssociateStats &associate_stats, - ViterbiStateEntry *parent_vse) { - float adjustment = 1.0f; - if (dawg_info == NULL || dawg_info->permuter != FREQ_DAWG_PERM) { - adjustment += language_model_penalty_non_freq_dict_word; - } - if (dawg_score == 0.0f) { - adjustment += language_model_penalty_non_dict_word; - if (length > language_model_min_compound_length) { - adjustment += ((length - language_model_min_compound_length) * - language_model_penalty_increment); +float LanguageModel::ComputeAdjustedPathCost(ViterbiStateEntry *vse) { + ASSERT_HOST(vse != NULL); + if (params_model_.Initialized()) { + float features[PTRAIN_NUM_FEATURE_TYPES]; + ExtractFeaturesFromPath(*vse, features); + float cost = params_model_.ComputeCost(features); + if (language_model_debug_level > 3) { + tprintf("ComputeAdjustedPathCost %g ParamsModel features:\n", cost); + if (language_model_debug_level >= 5) { + for (int f = 0; f < PTRAIN_NUM_FEATURE_TYPES; ++f) { + tprintf("%s=%g\n", kParamsTrainingFeatureTypeName[f], features[f]); + } + } } - } else if (dawg_score < 1.0f) { - adjustment += (1.0f - dawg_score) * language_model_penalty_non_dict_word; - } - if (associate_stats.shape_cost > 0) { - adjustment += associate_stats.shape_cost / static_cast(length); - } - if (language_model_ngram_on) { - ASSERT_HOST(ngram_info != NULL); - return ngram_info->ngram_cost * adjustment; + return cost * vse->outline_length; } else { - adjustment += ComputeConsistencyAdjustment(dawg_info, consistency_info); - return ratings_sum * adjustment; + float adjustment = 1.0f; + if (vse->dawg_info == NULL || vse->dawg_info->permuter != FREQ_DAWG_PERM) { + adjustment += language_model_penalty_non_freq_dict_word; + } + if (vse->dawg_info == NULL) { + adjustment += language_model_penalty_non_dict_word; + if (vse->length > language_model_min_compound_length) { + adjustment += ((vse->length - language_model_min_compound_length) * + language_model_penalty_increment); + } + } + if (vse->associate_stats.shape_cost > 0) { + adjustment += vse->associate_stats.shape_cost / + static_cast(vse->length); + } + if (language_model_ngram_on) { + ASSERT_HOST(vse->ngram_info != NULL); + return vse->ngram_info->ngram_and_classifier_cost * adjustment; + } else { + adjustment += ComputeConsistencyAdjustment(vse->dawg_info, + vse->consistency_info); + return vse->ratings_sum * adjustment; + } } } void LanguageModel::UpdateBestChoice( - BLOB_CHOICE *b, ViterbiStateEntry *vse, - HEAP *pain_points, - CHUNKS_RECORD *chunks_record, + LMPainPoints *pain_points, + WERD_RES *word_res, BestChoiceBundle *best_choice_bundle, BlamerBundle *blamer_bundle) { - int i; - BLOB_CHOICE_LIST_VECTOR temp_best_char_choices(vse->length); - for (i = 0; i < vse->length; ++i) { - temp_best_char_choices.push_back(NULL); - } - float *certainties = new float[vse->length]; - STATE temp_state; - // The fraction of letters in the path that are "covered" by dawgs. - // For space delimited languages this number will be 0.0 for nonwords - // and 1.0 for dictionary words. For non-space delimited languages - // this number will be in the [0.0, 1.0] range. - float dawg_score; bool truth_path; - WERD_CHOICE *word = ConstructWord(b, vse, chunks_record, - &temp_best_char_choices, certainties, - &dawg_score, &temp_state, + WERD_CHOICE *word = ConstructWord(vse, word_res, &best_choice_bundle->fixpt, blamer_bundle, &truth_path); ASSERT_HOST(word != NULL); - bool not_blaming = - (blamer_bundle == NULL || !blamer_bundle->segsearch_is_looking_for_blame); - - // Log new segmentation (for dict_->LogNewChoice()). - if (not_blaming) { - PIECES_STATE pieces_widths; - bin_to_pieces(&temp_state, chunks_record->ratings->dimension() - 1, - pieces_widths); - dict_->LogNewSegmentation(pieces_widths); - } - - if (language_model_debug_level > 0) { + if (dict_->stopper_debug_level >= 1) { STRING word_str; word->string_and_lengths(&word_str, NULL); - tprintf("UpdateBestChoice() constructed word %s\n", word_str.string()); - if (language_model_debug_level > 2) word->print(); - }; - - // Update raw_choice if needed. - if ((vse->top_choice_flags & kSmallestRatingFlag) && - word->rating() < best_choice_bundle->raw_choice->rating() && - not_blaming) { - dict_->LogNewChoice(1.0, certainties, true, word, temp_best_char_choices); - *(best_choice_bundle->raw_choice) = *word; - best_choice_bundle->raw_choice->set_permuter(TOP_CHOICE_PERM); - if (language_model_debug_level > 0) tprintf("Updated raw choice\n"); + vse->Print(word_str.string()); } - - // When working with non-space delimited languages we re-adjust the cost - // taking into account the final dawg_score and a more precise shape cost. - // While constructing the paths we assume that they are all dictionary words - // (since a single character would be a valid dictionary word). At the end - // we compute dawg_score which reflects how many characters on the path are - // "covered" by dictionary words of length > 1. - if (vse->associate_stats.full_wh_ratio_var != 0.0f || - (dict_->GetMaxFixedLengthDawgIndex() >= 0 && dawg_score < 1.0f)) { - vse->cost = ComputeAdjustedPathCost( - vse->ratings_sum, vse->length, dawg_score, vse->dawg_info, - vse->ngram_info, vse->consistency_info, vse->associate_stats, - vse->parent_vse); + if (language_model_debug_level > 0) { + word->print("UpdateBestChoice() constructed word"); + } + // Record features from the current path if necessary. + ParamsTrainingHypothesis curr_hyp; + if (blamer_bundle != NULL) { + if (vse->dawg_info != NULL) vse->dawg_info->permuter = + static_cast(word->permuter()); + ExtractFeaturesFromPath(*vse, curr_hyp.features); + word->string_and_lengths(&(curr_hyp.str), NULL); + curr_hyp.cost = vse->cost; // record cost for error rate computations if (language_model_debug_level > 0) { - tprintf("Updated vse cost to %g (dawg_score %g full_wh_ratio_var %g)\n", - vse->cost, dawg_score, vse->associate_stats.full_wh_ratio_var); + tprintf("Raw features extracted from %s (cost=%g) [ ", + curr_hyp.str.string(), curr_hyp.cost); + for (int deb_i = 0; deb_i < PTRAIN_NUM_FEATURE_TYPES; ++deb_i) { + tprintf("%g ", curr_hyp.features[deb_i]); + } + tprintf("]\n"); } - } - - // Update best choice and best char choices if needed. - // TODO(daria): re-write AcceptableChoice() and NoDangerousAmbig() - // to fit better into the new segmentation search. + // Record the current hypothesis in params_training_bundle. + blamer_bundle->AddHypothesis(curr_hyp); + if (truth_path) + blamer_bundle->UpdateBestRating(word->rating()); + } + if (blamer_bundle != NULL && blamer_bundle->GuidedSegsearchStillGoing()) { + // The word was constructed solely for blamer_bundle->AddHypothesis, so + // we no longer need it. + delete word; + return; + } + if (word_res->chopped_word != NULL && !word_res->chopped_word->blobs.empty()) + word->SetScriptPositions(false, word_res->chopped_word); + // Update and log new raw_choice if needed. + if (word_res->raw_choice == NULL || + word->rating() < word_res->raw_choice->rating()) { + if (word_res->LogNewRawChoice(word) && language_model_debug_level > 0) + tprintf("Updated raw choice\n"); + } + // Set the modified rating for best choice to vse->cost and log best choice. word->set_rating(vse->cost); - if (word->rating() < best_choice_bundle->best_choice->rating() && - not_blaming) { - dict_->LogNewChoice(vse->cost / (language_model_ngram_on ? - vse->ngram_info->ngram_cost : - vse->ratings_sum), - certainties, false, word, temp_best_char_choices); - // Since the rating of the word could have been modified by - // Dict::LogNewChoice() - check again. - if (word->rating() < best_choice_bundle->best_choice->rating()) { - bool modified_blobs; // not used - DANGERR fixpt; - if (dict_->AcceptableChoice(&temp_best_char_choices, word, &fixpt, - ASSOCIATOR_CALLER, &modified_blobs) && - AcceptablePath(*vse)) { - acceptable_choice_found_ = true; - } - // Update best_choice_bundle. - *(best_choice_bundle->best_choice) = *word; - best_choice_bundle->updated = true; - best_choice_bundle->best_char_choices->delete_data_pointers(); - best_choice_bundle->best_char_choices->clear(); - for (i = 0; i < temp_best_char_choices.size(); ++i) { - BLOB_CHOICE_LIST *cc_list = new BLOB_CHOICE_LIST(); - cc_list->deep_copy(temp_best_char_choices[i], &BLOB_CHOICE::deep_copy); - best_choice_bundle->best_char_choices->push_back(cc_list); - } - best_choice_bundle->best_state->part2 = temp_state.part2; - best_choice_bundle->best_state->part1 = temp_state.part1; - if (language_model_debug_level > 0) { - tprintf("Updated best choice\n"); - print_state("New state ", best_choice_bundle->best_state, - chunks_record->ratings->dimension()-1); - } - // Update hyphen state if we are dealing with a dictionary word. - if (vse->dawg_info != NULL && dict_->GetMaxFixedLengthDawgIndex() < 0) { - if (dict_->has_hyphen_end(*word)) { - dict_->set_hyphen_word(*word, *(dawg_args_->active_dawgs), - *(dawg_args_->constraints)); - } else { - dict_->reset_hyphen_vars(true); - } - } - best_choice_bundle->best_vse = vse; - best_choice_bundle->best_b = b; - best_choice_bundle->fixpt = fixpt; - - if (blamer_bundle != NULL) { - blamer_bundle->best_choice_is_dict_and_top_choice = - (vse->dawg_info != NULL && - dict_->GetMaxFixedLengthDawgIndex() < 0 && - (vse->top_choice_flags)); + // Call LogNewChoice() for best choice from Dict::adjust_word() since it + // computes adjust_factor that is used by the adaption code (e.g. by + // ClassifyAdaptableWord() to compute adaption acceptance thresholds). + // Note: the rating of the word is not adjusted. + dict_->adjust_word(word, vse->dawg_info == NULL, + vse->consistency_info.xht_decision, 0.0, + false, language_model_debug_level > 0); + // Hand ownership of the word over to the word_res. + if (!word_res->LogNewCookedChoice(dict_->tessedit_truncate_wordchoice_log, + dict_->stopper_debug_level >= 1, word)) { + // The word was so bad that it was deleted. + return; + } + if (word_res->best_choice == word) { + // Word was the new best. + if (dict_->AcceptableChoice(*word, vse->consistency_info.xht_decision) && + AcceptablePath(*vse)) { + acceptable_choice_found_ = true; + } + // Update best_choice_bundle. + best_choice_bundle->updated = true; + best_choice_bundle->best_vse = vse; + if (language_model_debug_level > 0) { + tprintf("Updated best choice\n"); + word->print_state("New state "); + } + // Update hyphen state if we are dealing with a dictionary word. + if (vse->dawg_info != NULL) { + if (dict_->has_hyphen_end(*word)) { + dict_->set_hyphen_word(*word, *(dawg_args_->active_dawgs)); + } else { + dict_->reset_hyphen_vars(true); } } - } - if (blamer_bundle != NULL) { - // Record the current hypothesis in params_training_bundle. - ParamsTrainingHypothesis &hyp = - blamer_bundle->params_training_bundle.AddHypothesis(); - word->string_and_lengths(&(hyp.str), NULL); - ExtractRawFeaturesFromPath(*vse, hyp.features); - if (truth_path && // update best truth path rating - word->rating() < blamer_bundle->best_correctly_segmented_rating) { - blamer_bundle->best_correctly_segmented_rating = word->rating(); + + if (blamer_bundle != NULL) { + blamer_bundle->set_best_choice_is_dict_and_top_choice( + vse->dawg_info != NULL && vse->top_choice_flags); } } - - // Clean up. - delete[] certainties; - delete word; + if (wordrec_display_segmentations) { + word->DisplaySegmentation(word_res->chopped_word); + } } -void LanguageModel::ExtractRawFeaturesFromPath(const ViterbiStateEntry &vse, - float *features) { - for (int f = 0; f < PTRAIN_NUM_RAW_FEATURE_TYPES; ++f) features[f] = 0.0; - // Dictionary-related features. +void LanguageModel::ExtractFeaturesFromPath( + const ViterbiStateEntry &vse, float features[]) { + memset(features, 0, sizeof(float) * PTRAIN_NUM_FEATURE_TYPES); + // Record dictionary match info. + int len = vse.length <= kMaxSmallWordUnichars ? 0 : + vse.length <= kMaxMediumWordUnichars ? 1 : 2; if (vse.dawg_info != NULL) { - features[PTRAIN_RAW_FEATURE_DICT_MATCH_TYPE] = vse.dawg_info->permuter; - - // Mark as unambiguous if unambig_dawg is found among active dawgs. - for (int d = 0; d < vse.dawg_info->active_dawgs->size(); ++d) { - if (dict_->GetDawg(vse.dawg_info->active_dawgs->get(d).dawg_index) == - dict_->GetUnambigDawg()) { - features[PTRAIN_RAW_FEATURE_UNAMBIG_DICT_MATCH] = 1.0; - break; + int permuter = vse.dawg_info->permuter; + if (permuter == NUMBER_PERM || permuter == USER_PATTERN_PERM) { + if (vse.consistency_info.num_digits == vse.length) { + features[PTRAIN_DIGITS_SHORT+len] = 1.0; + } else { + features[PTRAIN_NUM_SHORT+len] = 1.0; } - } - } - if (vse.associate_stats.shape_cost > 0) { - features[PTRAIN_RAW_FEATURE_SHAPE_COST] = vse.associate_stats.shape_cost; - } - if (language_model_ngram_on) { - ASSERT_HOST(vse.ngram_info != NULL); - features[PTRAIN_RAW_FEATURE_NGRAM_PROB] = vse.ngram_info->ngram_prob; - } - // Consistency-related features. - features[PTRAIN_RAW_FEATURE_NUM_BAD_PUNC] = - vse.consistency_info.NumInconsistentPunc(); - features[PTRAIN_RAW_FEATURE_NUM_BAD_CASE] = - vse.consistency_info.NumInconsistentCase(); - features[PTRAIN_RAW_FEATURE_NUM_BAD_CHAR_TYPE] = - vse.consistency_info.NumInconsistentChartype(); - features[PTRAIN_RAW_FEATURE_NUM_BAD_SPACING] = + } else if (permuter == DOC_DAWG_PERM) { + features[PTRAIN_DOC_SHORT+len] = 1.0; + } else if (permuter == SYSTEM_DAWG_PERM || permuter == USER_DAWG_PERM || + permuter == COMPOUND_PERM) { + features[PTRAIN_DICT_SHORT+len] = 1.0; + } else if (permuter == FREQ_DAWG_PERM) { + features[PTRAIN_FREQ_SHORT+len] = 1.0; + } + } + // Record shape cost feature (normalized by path length). + features[PTRAIN_SHAPE_COST_PER_CHAR] = + vse.associate_stats.shape_cost / static_cast(vse.length); + // Record ngram cost. (normalized by the path length). + features[PTRAIN_NGRAM_COST_PER_CHAR] = 0.0; + if (vse.ngram_info != NULL) { + features[PTRAIN_NGRAM_COST_PER_CHAR] = + vse.ngram_info->ngram_cost / static_cast(vse.length); + } + // Record consistency-related features. + // Disabled this feature for due to its poor performance. + // features[PTRAIN_NUM_BAD_PUNC] = vse.consistency_info.NumInconsistentPunc(); + features[PTRAIN_NUM_BAD_CASE] = vse.consistency_info.NumInconsistentCase(); + features[PTRAIN_XHEIGHT_CONSISTENCY] = vse.consistency_info.xht_decision; + features[PTRAIN_NUM_BAD_CHAR_TYPE] = vse.dawg_info == NULL ? + vse.consistency_info.NumInconsistentChartype() : 0.0; + features[PTRAIN_NUM_BAD_SPACING] = vse.consistency_info.NumInconsistentSpaces(); - features[PTRAIN_RAW_FEATURE_NUM_BAD_SCRIPT] = - vse.consistency_info.inconsistent_script; - features[PTRAIN_RAW_FEATURE_NUM_BAD_FONT] = - vse.consistency_info.inconsistent_font; + // Disabled this feature for now due to its poor performance. + // features[PTRAIN_NUM_BAD_FONT] = vse.consistency_info.inconsistent_font; + // Classifier-related features. - features[PTRAIN_RAW_FEATURE_WORST_CERT] = vse.min_certainty; - features[PTRAIN_RAW_FEATURE_RATING] = vse.ratings_sum; - features[PTRAIN_RAW_FEATURE_ADAPTED] = vse.adapted; - // Normalization-related features. - features[PTRAIN_RAW_FEATURE_NUM_UNICHARS] = vse.length; - features[PTRAIN_RAW_FEATURE_OUTLINE_LEN] = vse.outline_length; + features[PTRAIN_RATING_PER_CHAR] = + vse.ratings_sum / static_cast(vse.outline_length); } WERD_CHOICE *LanguageModel::ConstructWord( - BLOB_CHOICE *b, ViterbiStateEntry *vse, - CHUNKS_RECORD *chunks_record, - BLOB_CHOICE_LIST_VECTOR *best_char_choices, - float certainties[], - float *dawg_score, - STATE *state, + WERD_RES *word_res, + DANGERR *fixpt, BlamerBundle *blamer_bundle, bool *truth_path) { - uinT64 state_uint = 0x0; if (truth_path != NULL) { *truth_path = (blamer_bundle != NULL && - !blamer_bundle->correct_segmentation_cols.empty() && - vse->length == blamer_bundle->correct_segmentation_cols.length()); + vse->length == blamer_bundle->correct_segmentation_length()); } - const uinT64 kHighestBitOn = 0x8000000000000000LL; - BLOB_CHOICE *curr_b = b; - LanguageModelState *curr_lms = - reinterpret_cast(b->language_model_state()); + BLOB_CHOICE *curr_b = vse->curr_b; ViterbiStateEntry *curr_vse = vse; int i; bool compound = dict_->hyphenated(); // treat hyphenated words as compound - bool dawg_score_done = true; - if (dawg_score != NULL) { - *dawg_score = 0.0f; - // For space-delimited languages the presence of dawg_info in the last - // ViterbyStateEntry on the path means that the whole path represents - // a valid dictionary word. - if (dict_->GetMaxFixedLengthDawgIndex() < 0) { - if (vse->dawg_info != NULL) *dawg_score = 1.0f; - } else if (vse->length == 1) { - *dawg_score = 1.0f; // each one-letter word is legal - dawg_score_done = true; // in non-space delimited languages - } else { - dawg_score_done = false; // do more work to compute dawg_score - } - } - // For non-space delimited languages we compute the fraction of letters - // "covered" by fixed length dawgs (i.e. words of length > 1 on the path). - int covered_by_fixed_length_dawgs = 0; - // The number of unichars remaining that should be skipped because - // they are covered by the previous word from fixed length dawgs. - int fixed_length_num_unichars_to_skip = 0; // Re-compute the variance of the width-to-height ratios (since we now // can compute the mean over the whole word). @@ -1430,47 +1385,18 @@ WERD_CHOICE *LanguageModel::ConstructWord( } // Construct a WERD_CHOICE by tracing parent pointers. - WERD_CHOICE *word = new WERD_CHOICE(chunks_record->word_res->uch_set, - vse->length); + WERD_CHOICE *word = new WERD_CHOICE(word_res->uch_set, vse->length); word->set_length(vse->length); + int total_blobs = 0; for (i = (vse->length-1); i >= 0; --i) { - if (blamer_bundle != NULL && truth_path != NULL && *truth_path) { - if ((blamer_bundle->correct_segmentation_cols[i] != - curr_lms->contained_in_col) || - (blamer_bundle->correct_segmentation_rows[i] != - curr_lms->contained_in_row)) { + if (blamer_bundle != NULL && truth_path != NULL && *truth_path && + !blamer_bundle->MatrixPositionCorrect(i, curr_b->matrix_cell())) { *truth_path = false; - } - } - word->set_unichar_id(curr_b->unichar_id(), i); - word->set_fragment_length(1, i); - if (certainties != NULL) certainties[i] = curr_b->certainty(); - if (best_char_choices != NULL) { - best_char_choices->set(chunks_record->ratings->get( - curr_lms->contained_in_col, curr_lms->contained_in_row), i); - } - if (state != NULL) { - // Record row minus col zeroes in the reverse state to mark the number - // of joins done by using a blob from this cell in the ratings matrix. - state_uint >>= (curr_lms->contained_in_row - curr_lms->contained_in_col); - // Record a one in the reverse state to indicate the split before - // the blob from the next cell in the ratings matrix (unless we are - // at the first cell, in which case there is no next blob). - if (i != 0) { - state_uint >>= 1; - state_uint |= kHighestBitOn; - } - } - // For non-space delimited languages: find word endings recorded while - // trying to separate the current path into words (for words found in - // fixed length dawgs. - if (!dawg_score_done && curr_vse->dawg_info != NULL) { - UpdateCoveredByFixedLengthDawgs(*(curr_vse->dawg_info->active_dawgs), - i, vse->length, - &fixed_length_num_unichars_to_skip, - &covered_by_fixed_length_dawgs, - dawg_score, &dawg_score_done); } + // The number of blobs used for this choice is row - col + 1. + int num_blobs = curr_b->matrix_cell().row - curr_b->matrix_cell().col + 1; + total_blobs += num_blobs; + word->set_blob_choice(i, num_blobs, curr_b); // Update the width-to-height ratio variance. Useful non-space delimited // languages to ensure that the blobs are of uniform width. // Skip leading and trailing punctuation when computing the variance. @@ -1492,27 +1418,22 @@ WERD_CHOICE *LanguageModel::ConstructWord( curr_vse->dawg_info->permuter == COMPOUND_PERM) compound = true; // Update curr_* pointers. - if (curr_vse->parent_b == NULL) break; - curr_b = curr_vse->parent_b; - curr_lms = - reinterpret_cast(curr_b->language_model_state()); curr_vse = curr_vse->parent_vse; + if (curr_vse == NULL) break; + curr_b = curr_vse->curr_b; } - ASSERT_HOST(i == 0); // check that we recorded all the unichar ids + ASSERT_HOST(i == 0); // check that we recorded all the unichar ids. + ASSERT_HOST(total_blobs == word_res->ratings->dimension()); // Re-adjust shape cost to include the updated width-to-height variance. if (full_wh_ratio_mean != 0.0f) { vse->associate_stats.shape_cost += vse->associate_stats.full_wh_ratio_var; } - if (state != NULL) { - state_uint >>= (64 - (chunks_record->ratings->dimension()-1)); - state->part2 = state_uint; - state_uint >>= 32; - state->part1 = state_uint; - } word->set_rating(vse->ratings_sum); word->set_certainty(vse->min_certainty); - if (vse->dawg_info != NULL && dict_->GetMaxFixedLengthDawgIndex() < 0) { + word->set_x_heights(vse->consistency_info.BodyMinXHeight(), + vse->consistency_info.BodyMaxXHeight()); + if (vse->dawg_info != NULL) { word->set_permuter(compound ? COMPOUND_PERM : vse->dawg_info->permuter); } else if (language_model_ngram_on && !vse->ngram_info->pruned) { word->set_permuter(NGRAM_PERM); @@ -1521,485 +1442,9 @@ WERD_CHOICE *LanguageModel::ConstructWord( } else { word->set_permuter(NO_PERM); } + word->set_dangerous_ambig_found_(!dict_->NoDangerousAmbig(word, fixpt, true, + word_res->ratings)); return word; } -void LanguageModel::UpdateCoveredByFixedLengthDawgs( - const DawgInfoVector &active_dawgs, int word_index, int word_length, - int *skip, int *covered, float *dawg_score, bool *dawg_score_done) { - if (language_model_debug_level > 3) { - tprintf("UpdateCoveredByFixedLengthDawgs for index %d skip=%d\n", - word_index, *skip, word_length); - } - - if (*skip > 0) { - --(*skip); - } else { - int best_index = -1; - for (int d = 0; d < active_dawgs.size(); ++d) { - int dawg_index = (active_dawgs[d]).dawg_index; - if (dawg_index > dict_->GetMaxFixedLengthDawgIndex()) { - // If active_dawgs of the last ViterbiStateEntry on the path - // contain a non-fixed length dawg, this means that the whole - // path represents a word from a non-fixed length word dawg. - if (word_index == (word_length - 1)) { - *dawg_score = 1.0f; - *dawg_score_done = true; - return; - } - } else if (dawg_index >= kMinFixedLengthDawgLength) { - const Dawg *curr_dawg = dict_->GetDawg(dawg_index); - ASSERT_HOST(curr_dawg != NULL); - if ((active_dawgs[d]).ref != NO_EDGE && - curr_dawg->end_of_word((active_dawgs[d]).ref) && - dawg_index > best_index) { - best_index = dawg_index; - } - - if (language_model_debug_level > 3) { - tprintf("dawg_index %d, ref %d, eow %d\n", dawg_index, - (active_dawgs[d]).ref, - ((active_dawgs[d]).ref != NO_EDGE && - curr_dawg->end_of_word((active_dawgs[d]).ref))); - } - } - } // end for - if (best_index != -1) { - *skip = best_index - 1; - *covered += best_index; - } - } // end if/else skip - - if (word_index == 0) { - ASSERT_HOST(*covered <= word_length); - *dawg_score = (static_cast(*covered) / - static_cast(word_length)); - *dawg_score_done = true; - } -} - -void LanguageModel::GeneratePainPointsFromColumn( - int col, - const GenericVector &non_empty_rows, - float best_choice_cert, - HEAP *pain_points, - BestPathByColumn *best_path_by_column[], - CHUNKS_RECORD *chunks_record) { - for (int i = 0; i < non_empty_rows.length(); ++i) { - int row = non_empty_rows[i]; - if (language_model_debug_level > 0) { - tprintf("\nLooking for pain points in col=%d row=%d\n", col, row); - } - if (language_model_ngram_on) { - GenerateNgramModelPainPointsFromColumn( - col, row, pain_points, chunks_record); - } else { - GenerateProblematicPathPainPointsFromColumn( - col, row, best_choice_cert, pain_points, - best_path_by_column, chunks_record); - } - } -} - -void LanguageModel::GenerateNgramModelPainPointsFromColumn( - int col, int row, HEAP *pain_points, CHUNKS_RECORD *chunks_record) { - // Find the first top choice path recorded for this cell. - // If this path is pruned - generate a pain point. - ASSERT_HOST(chunks_record->ratings->get(col, row) != NULL); - BLOB_CHOICE_IT bit(chunks_record->ratings->get(col, row)); - bool fragmented = false; - for (bit.mark_cycle_pt(); !bit.cycled_list(); bit.forward()) { - if (dict_->getUnicharset().get_fragment(bit.data()->unichar_id())) { - fragmented = true; - continue; - } - LanguageModelState *lms = reinterpret_cast( - bit.data()->language_model_state()); - if (lms == NULL) continue; - ViterbiStateEntry_IT vit(&(lms->viterbi_state_entries)); - for (vit.mark_cycle_pt(); !vit.cycled_list(); vit.forward()) { - const ViterbiStateEntry *vse = vit.data(); - if (!vse->top_choice_flags) continue; - ASSERT_HOST(vse->ngram_info != NULL); - if (vse->ngram_info->pruned && (vse->parent_vse == NULL || - !vse->parent_vse->ngram_info->pruned)) { - if (vse->parent_vse != NULL) { - LanguageModelState *pp_lms = reinterpret_cast( - vse->parent_b->language_model_state()); - GeneratePainPoint(pp_lms->contained_in_col, row, false, - kDefaultPainPointPriorityAdjustment, - -1.0f, fragmented, -1.0f, - max_char_wh_ratio_, - vse->parent_vse->parent_b, - vse->parent_vse->parent_vse, - chunks_record, pain_points); - } - if (vse->parent_vse != NULL && - vse->parent_vse->parent_vse != NULL && - dict_->getUnicharset().get_ispunctuation( - vse->parent_b->unichar_id())) { - // If the dip in the ngram probability is due to punctuation in the - // middle of the word - go two unichars back to combine this - // punctuation mark with the previous character on the path. - LanguageModelState *pp_lms = reinterpret_cast( - vse->parent_vse->parent_b->language_model_state()); - GeneratePainPoint(pp_lms->contained_in_col, col-1, false, - kDefaultPainPointPriorityAdjustment, - -1.0f, fragmented, -1.0f, - max_char_wh_ratio_, - vse->parent_vse->parent_vse->parent_b, - vse->parent_vse->parent_vse->parent_vse, - chunks_record, pain_points); - } else if (row+1 < chunks_record->ratings->dimension()) { - GeneratePainPoint(col, row+1, true, - kDefaultPainPointPriorityAdjustment, - -1.0f, fragmented, -1.0f, - max_char_wh_ratio_, - vse->parent_b, - vse->parent_vse, - chunks_record, pain_points); - } - } - return; // examined the lowest cost top choice path - } - } -} - -void LanguageModel::GenerateProblematicPathPainPointsFromColumn( - int col, int row, float best_choice_cert, - HEAP *pain_points, BestPathByColumn *best_path_by_column[], - CHUNKS_RECORD *chunks_record) { - MATRIX *ratings = chunks_record->ratings; - - // Get the best path from this matrix cell. - BLOB_CHOICE_LIST *blist = ratings->get(col, row); - ASSERT_HOST(blist != NULL); - if (blist->empty()) return; - BLOB_CHOICE_IT bit(blist); - bool fragment = false; - while (dict_->getUnicharset().get_fragment(bit.data()->unichar_id()) && - !bit.at_last()) { // skip fragments - fragment = true; - bit.forward(); - } - if (bit.data()->language_model_state() == NULL) return; - ViterbiStateEntry_IT vit(&(reinterpret_cast( - bit.data()->language_model_state())->viterbi_state_entries)); - if (vit.empty()) return; - ViterbiStateEntry *vse = vit.data(); - // Check whether this path is promising. - bool path_is_promising = true; - if (vse->parent_vse != NULL) { - float potential_avg_cost = - ((vse->parent_vse->cost + bit.data()->rating()*0.5f) / - static_cast(row+1)); - if (language_model_debug_level > 0) { - tprintf("potential_avg_cost %g best cost %g\n", - potential_avg_cost, (*best_path_by_column)[col].avg_cost); - } - if (potential_avg_cost >= (*best_path_by_column)[col].avg_cost) { - path_is_promising = false; - } - } - // Set best_parent_vse to the best parent recorded in best_path_by_column. - ViterbiStateEntry *best_parent_vse = vse->parent_vse; - BLOB_CHOICE *best_parent_b = vse->parent_b; - if (col > 0 && (*best_path_by_column)[col-1].best_vse != NULL) { - ASSERT_HOST((*best_path_by_column)[col-1].best_b != NULL); - LanguageModelState *best_lms = reinterpret_cast( - ((*best_path_by_column)[col-1].best_b)->language_model_state()); - if (best_lms->contained_in_row == col-1) { - best_parent_vse = (*best_path_by_column)[col-1].best_vse; - best_parent_b = (*best_path_by_column)[col-1].best_b; - if (language_model_debug_level > 0) { - tprintf("Setting best_parent_vse to %p\n", best_parent_vse); - } - } - } - // Check whether this entry terminates the best parent path - // recorded in best_path_by_column. - bool best_not_prolonged = (best_parent_vse != vse->parent_vse); - - // If this path is problematic because of the last unichar - generate - // a pain point to combine it with its left and right neighbor. - BLOB_CHOICE_IT tmp_bit; - if (best_not_prolonged || - (path_is_promising && - ProblematicPath(*vse, bit.data()->unichar_id(), - row+1 == ratings->dimension()))) { - float worst_piece_cert; - bool fragmented; - if (col-1 > 0) { - GetWorstPieceCertainty(col-1, row, chunks_record->ratings, - &worst_piece_cert, &fragmented); - GeneratePainPoint(col-1, row, false, - kDefaultPainPointPriorityAdjustment, - worst_piece_cert, fragmented, best_choice_cert, - max_char_wh_ratio_, best_parent_b, best_parent_vse, - chunks_record, pain_points); - } - if (row+1 < ratings->dimension()) { - GetWorstPieceCertainty(col, row+1, chunks_record->ratings, - &worst_piece_cert, &fragmented); - GeneratePainPoint(col, row+1, true, kDefaultPainPointPriorityAdjustment, - worst_piece_cert, fragmented, best_choice_cert, - max_char_wh_ratio_, best_parent_b, best_parent_vse, - chunks_record, pain_points); - } - } // for ProblematicPath -} - -void LanguageModel::GeneratePainPointsFromBestChoice( - HEAP *pain_points, - CHUNKS_RECORD *chunks_record, - BestChoiceBundle *best_choice_bundle) { - // Variables to backtrack best_vse path; - ViterbiStateEntry *curr_vse = best_choice_bundle->best_vse; - BLOB_CHOICE *curr_b = best_choice_bundle->best_b; - - // Begins and ends in DANGERR vector record the positions in the blob choice - // list of the best choice. We need to translate these endpoints into the - // beginning column and ending row for the pain points. We maintain - // danger_begin and danger_end arrays indexed by the position in - // best_choice_bundle->best_char_choices (which is equal to the position - // on the best_choice_bundle->best_vse path). - // danger_end[d] stores the DANGERR_INFO structs with end==d and is - // initialized at the beginning of this function. - // danger_begin[d] stores the DANGERR_INFO struct with begin==d and - // has end set to the row of the end of this ambiguity. - // The translation from end in terms of the best choice index to the end row - // is done while following the parents of best_choice_bundle->best_vse. - assert(best_choice_bundle->best_char_choices->length() == - best_choice_bundle->best_vse->length); - DANGERR *danger_begin = NULL; - DANGERR *danger_end = NULL; - int d; - if (!best_choice_bundle->fixpt.empty()) { - danger_begin = new DANGERR[best_choice_bundle->best_vse->length]; - danger_end = new DANGERR[best_choice_bundle->best_vse->length]; - for (d = 0; d < best_choice_bundle->fixpt.size(); ++d) { - const DANGERR_INFO &danger = best_choice_bundle->fixpt[d]; - // Only use n->1 ambiguities. - if (danger.end > danger.begin && !danger.correct_is_ngram && - (!language_model_ngram_on || danger.dangerous)) { - danger_end[danger.end].push_back(danger); - } - } - } - - // Variables to keep track of punctuation/number streaks. - int punc_streak_end_row = -1; - int punc_streak_length = 0; - float punc_streak_min_cert = 0.0f; - - if (language_model_debug_level > 0) { - tprintf("\nGenerating pain points for best path=%p\n", curr_vse); - } - - int word_index = best_choice_bundle->best_vse->length; - while (curr_vse != NULL) { - word_index--; - ASSERT_HOST(word_index >= 0); - ASSERT_HOST(curr_b != NULL); - if (language_model_debug_level > 0) { - tprintf("Looking at unichar %s\n", - dict_->getUnicharset().id_to_unichar(curr_b->unichar_id())); - } - - int pp_col = reinterpret_cast( - curr_b->language_model_state())->contained_in_col; - int pp_row = reinterpret_cast( - curr_b->language_model_state())->contained_in_row; - - // Generate pain points for ambiguities found by NoDangerousAmbig(). - if (danger_end != NULL) { - // Translate end index of an ambiguity to an end row. - for (d = 0; d < danger_end[word_index].size(); ++d) { - danger_end[word_index][d].end = pp_row; - danger_begin[danger_end[word_index][d].begin].push_back( - danger_end[word_index][d]); - } - // Generate a pain point to combine unchars in the "wrong" part - // of the ambiguity. - for (d = 0; d < danger_begin[word_index].size(); ++d) { - if (language_model_debug_level > 0) { - tprintf("Generating pain point from %sambiguity\n", - danger_begin[word_index][d].dangerous ? "dangerous " : ""); - } - GeneratePainPoint(pp_col, danger_begin[word_index][d].end, false, - danger_begin[word_index][d].dangerous ? - kCriticalPainPointPriorityAdjustment : - kBestChoicePainPointPriorityAdjustment, - best_choice_bundle->best_choice->certainty(), true, - best_choice_bundle->best_choice->certainty(), - kLooseMaxCharWhRatio, - curr_vse->parent_b, curr_vse->parent_vse, - chunks_record, pain_points); - } - } - - if (!language_model_ngram_on) { // no need to use further heuristics if we - // are guided by the character ngram model - // Generate pain points for problematic sub-paths. - if (ProblematicPath(*curr_vse, curr_b->unichar_id(), - pp_row+1 == chunks_record->ratings->dimension())) { - if (language_model_debug_level > 0) { - tprintf("Generating pain point from a problematic sub-path\n"); - } - float worst_piece_cert; - bool fragment; - if (pp_col > 0) { - GetWorstPieceCertainty(pp_col-1, pp_row, chunks_record->ratings, - &worst_piece_cert, &fragment); - GeneratePainPoint(pp_col-1, pp_row, false, - kBestChoicePainPointPriorityAdjustment, - worst_piece_cert, true, - best_choice_bundle->best_choice->certainty(), - max_char_wh_ratio_, NULL, NULL, - chunks_record, pain_points); - } - if (pp_row+1 < chunks_record->ratings->dimension()) { - GetWorstPieceCertainty(pp_col, pp_row+1, chunks_record->ratings, - &worst_piece_cert, &fragment); - GeneratePainPoint(pp_col, pp_row+1, true, - kBestChoicePainPointPriorityAdjustment, - worst_piece_cert, true, - best_choice_bundle->best_choice->certainty(), - max_char_wh_ratio_, NULL, NULL, - chunks_record, pain_points); - } - } - - // Generate a pain point if we encountered a punctuation/number streak to - // combine all punctuation marks into one blob and try to classify it. - bool is_alpha = dict_->getUnicharset().get_isalpha(curr_b->unichar_id()); - if (!is_alpha) { - if (punc_streak_end_row == -1) punc_streak_end_row = pp_row; - punc_streak_length++; - if (curr_b->certainty() < punc_streak_min_cert) - punc_streak_min_cert = curr_b->certainty(); - } - if (is_alpha || curr_vse->parent_vse == NULL) { - if (punc_streak_end_row != -1 && punc_streak_length > 1) { - if (language_model_debug_level > 0) { - tprintf("Generating pain point from a punctuation streak\n"); - } - if (is_alpha || - (curr_vse->parent_vse == NULL && punc_streak_length > 2)) { - GeneratePainPoint(pp_row+1, punc_streak_end_row, false, - kBestChoicePainPointPriorityAdjustment, - punc_streak_min_cert, true, - best_choice_bundle->best_choice->certainty(), - max_char_wh_ratio_, curr_b, curr_vse, - chunks_record, pain_points); - } - // Handle punctuation/number streaks starting from the first unichar. - if (curr_vse->parent_vse == NULL) { - GeneratePainPoint(0, punc_streak_end_row, false, - kBestChoicePainPointPriorityAdjustment, - punc_streak_min_cert, true, - best_choice_bundle->best_choice->certainty(), - max_char_wh_ratio_, NULL, NULL, - chunks_record, pain_points); - } - } - punc_streak_end_row = -1; - punc_streak_length = 0; - punc_streak_min_cert = 0.0f; - } // end handling punctuation streaks - } - - curr_b = curr_vse->parent_b; - curr_vse = curr_vse->parent_vse; - } // end looking at best choice subpaths - - // Clean up. - if (danger_end != NULL) { - delete[] danger_begin; - delete[] danger_end; - } -} - -bool LanguageModel::GeneratePainPoint( - int col, int row, bool ok_to_extend, float priority, - float worst_piece_cert, bool fragmented, float best_choice_cert, - float max_char_wh_ratio, - BLOB_CHOICE *parent_b, ViterbiStateEntry *parent_vse, - CHUNKS_RECORD *chunks_record, HEAP *pain_points) { - if (col < 0 || row >= chunks_record->ratings->dimension() || - chunks_record->ratings->get(col, row) != NOT_CLASSIFIED) { - return false; - } - if (language_model_debug_level > 3) { - tprintf("\nGenerating pain point for col=%d row=%d priority=%g parent=", - col, row, priority); - if (parent_vse != NULL) { - PrintViterbiStateEntry("", parent_vse, parent_b, chunks_record); - } else { - tprintf("NULL"); - } - tprintf("\n"); - } - - AssociateStats associate_stats; - ComputeAssociateStats(col, row, max_char_wh_ratio, parent_vse, - chunks_record, &associate_stats); - // For fixed-pitch fonts/languages: if the current combined blob overlaps - // the next blob on the right and it is ok to extend the blob, try expending - // the blob until there is no overlap with the next blob on the right or - // until the width-to-height ratio becomes too large. - if (ok_to_extend) { - while (associate_stats.bad_fixed_pitch_right_gap && - row+1 < chunks_record->ratings->dimension() && - !associate_stats.bad_fixed_pitch_wh_ratio) { - ComputeAssociateStats(col, ++row, max_char_wh_ratio, parent_vse, - chunks_record, &associate_stats); - } - } - - if (associate_stats.bad_shape) { - if (language_model_debug_level > 3) { - tprintf("Discarded pain point with a bad shape\n"); - } - return false; - } - - // Compute pain point priority. - if (associate_stats.shape_cost > 0) { - priority *= associate_stats.shape_cost; - } - if (worst_piece_cert < best_choice_cert) { - worst_piece_cert = best_choice_cert; - } - priority *= CertaintyScore(worst_piece_cert); - if (fragmented) priority /= kDefaultPainPointPriorityAdjustment; - - if (language_model_debug_level > 3) { - tprintf("worst_piece_cert=%g fragmented=%d\n", - worst_piece_cert, fragmented); - } - - if (parent_vse != NULL) { - priority *= sqrt(parent_vse->cost / static_cast(col)); - if (parent_vse->dawg_info != NULL) { - priority /= kDefaultPainPointPriorityAdjustment; - if (parent_vse->length > language_model_min_compound_length) { - priority /= sqrt(static_cast(parent_vse->length)); - } - } - } - - MATRIX_COORD *pain_point = new MATRIX_COORD(col, row); - if (HeapPushCheckSize(pain_points, priority, pain_point)) { - if (language_model_debug_level) { - tprintf("Added pain point with priority %g\n", priority); - } - return true; - } else { - delete pain_point; - if (language_model_debug_level) tprintf("Pain points heap is full\n"); - return false; - } -} - } // namespace tesseract diff --git a/wordrec/language_model.h b/wordrec/language_model.h index 19e36b1fc0..f0acfff5cd 100644 --- a/wordrec/language_model.h +++ b/wordrec/language_model.h @@ -1,7 +1,8 @@ /////////////////////////////////////////////////////////////////////// // File: language_model.h // Description: Functions that utilize the knowledge about the properties, -// structure and statistics of the language to help recognition. +// structure and statistics of the language to help segmentation +// search. // Author: Daria Antonova // Created: Mon Nov 11 11:26:43 PST 2009 // @@ -26,288 +27,47 @@ #include "dict.h" #include "fontinfo.h" #include "intproto.h" +#include "lm_consistency.h" +#include "lm_pain_points.h" +#include "lm_state.h" #include "matrix.h" -#include "oldheap.h" #include "params.h" #include "pageres.h" +#include "params_model.h" namespace tesseract { -// Used for expressing various language model flags. -typedef unsigned char LanguageModelFlagsType; - -// Struct for keeping track of the consistency of the path. -struct LanguageModelConsistencyInfo { - LanguageModelConsistencyInfo() - : punc_ref(NO_EDGE), num_punc(0), invalid_punc(false), - num_non_first_upper(0), num_lower(0), - script_id(0), inconsistent_script(false), - num_alphas(0), num_digits(0), num_other(0), - num_inconsistent_spaces(0), inconsistent_font(false) {} - inline int NumInconsistentPunc() const { - return invalid_punc ? num_punc : 0; - } - inline int NumInconsistentCase() const { - return (num_non_first_upper > num_lower) ? num_lower : num_non_first_upper; - } - inline int NumInconsistentChartype() const { - return (NumInconsistentPunc() + num_other + - ((num_alphas > num_digits) ? num_digits : num_alphas)); - } - inline bool Consistent() const { - return (NumInconsistentPunc() == 0 && NumInconsistentCase() == 0 && - NumInconsistentChartype() == 0 && !inconsistent_script); - } - inline int NumInconsistentSpaces() const { - return num_inconsistent_spaces; - } - - EDGE_REF punc_ref; - int num_punc; - bool invalid_punc; - int num_non_first_upper; - int num_lower; - int script_id; - bool inconsistent_script; - int num_alphas; - int num_digits; - int num_other; - int num_inconsistent_spaces; - bool inconsistent_font; -}; - - -// The following structs are used for storing the state of the language model -// in the segmentation search graph. In this graph the nodes are BLOB_CHOICEs -// and the links are the replationships between the underlying blobs (see -// segsearch.h for a more detailed description). -// Each of the BLOB_CHOICEs contains LanguageModelState struct, which has -// a list of N best paths (list of ViterbiStateEntry) explored by the Viterbi -// search leading up to and including this BLOB_CHOICE. -// Each ViterbiStateEntry contains information from various components of the -// language model: dawgs in which the path is found, character ngram model -// probability of the path, script/chartype/font consistency info, state for -// language-specific heuristics (e.g. hyphenated and compund words, lower/upper -// case preferences, etc). -// Each ViterbiStateEntry also contains the parent pointer, so that the path -// that it represents (WERD_CHOICE) can be constructed by following these -// parent pointers. - -// Struct for storing additional information used by Dawg language model -// component. It stores the set of active dawgs in which the sequence of -// letters on a path can be found and the constraints that have to be -// satisfied at the end of the word (e.g. beginning/ending punctuation). -struct LanguageModelDawgInfo { - LanguageModelDawgInfo(DawgInfoVector *a, DawgInfoVector *c, - PermuterType pt) : permuter(pt) { - active_dawgs = new DawgInfoVector(*a); - constraints = new DawgInfoVector(*c); - } - ~LanguageModelDawgInfo() { - delete active_dawgs; - delete constraints; - } - DawgInfoVector *active_dawgs; - DawgInfoVector *constraints; - PermuterType permuter; -}; - -// Struct for storing additional information used by Ngram language model -// component. -struct LanguageModelNgramInfo { - LanguageModelNgramInfo(const char *c, int l, bool p, float np, float nc) - : context(c), context_unichar_step_len(l), pruned(p), ngram_prob(np), - ngram_cost(nc) {} - STRING context; // context string - // Length of the context measured by advancing using UNICHAR::utf8_step() - // (should be at most the order of the character ngram model used). - int context_unichar_step_len; - // The paths with pruned set are pruned out from the perspective of the - // character ngram model. They are explored further because they represent - // a dictionary match or a top choice. Thus ngram_info is still computed - // for them in order to calculate the combined cost. - bool pruned; - // -ln(P_ngram_model(path)) - float ngram_prob; - // -[ ln(P_classifier(path)) + scale_factor * ln(P_ngram_model(path)) ] - float ngram_cost; -}; - -// Struct for storing the information about a path in the segmentation graph -// explored by Viterbi search. -struct ViterbiStateEntry : public ELIST_LINK { - ViterbiStateEntry(BLOB_CHOICE *pb, ViterbiStateEntry *pe, - BLOB_CHOICE *b, float c, float ol, - const LanguageModelConsistencyInfo &ci, - const AssociateStats &as, - LanguageModelFlagsType tcf, - LanguageModelDawgInfo *d, LanguageModelNgramInfo *n) - : cost(c), parent_b(pb), parent_vse(pe), ratings_sum(b->rating()), - min_certainty(b->certainty()), adapted(b->adapted()), length(1), - outline_length(ol), consistency_info(ci), associate_stats(as), - top_choice_flags(tcf), dawg_info(d), ngram_info(n), updated(true) { - if (pe != NULL) { - ratings_sum += pe->ratings_sum; - if (pe->min_certainty < min_certainty) { - min_certainty = pe->min_certainty; - } - adapted += pe->adapted; - length += pe->length; - outline_length += pe->outline_length; - } - } - ~ViterbiStateEntry() { - delete dawg_info; - delete ngram_info; - } - // Comparator function for sorting ViterbiStateEntry_LISTs in - // non-increasing order of costs. - static int Compare(const void *e1, const void *e2) { - const ViterbiStateEntry *ve1 = - *reinterpret_cast(e1); - const ViterbiStateEntry *ve2 = - *reinterpret_cast(e2); - return (ve1->cost < ve2->cost) ? -1 : 1; - } - inline bool Consistent() const { - if (dawg_info != NULL && consistency_info.NumInconsistentCase() == 0) { - return true; - } - return consistency_info.Consistent(); - } - - // The cost is an adjusted ratings sum, that is adjusted by all the language - // model components that use Viterbi search. - float cost; - - // Pointers to parent BLOB_CHOICE and ViterbiStateEntry (not owned by this). - BLOB_CHOICE *parent_b; - ViterbiStateEntry *parent_vse; - - // Various information about the characters on the path represented - // by this ViterbiStateEntry. - float ratings_sum; // sum of ratings of character on the path - float min_certainty; // minimum certainty on the path - int adapted; // number of BLOB_CHOICES from adapted templates - int length; // number of characters on the path - float outline_length; // length of the outline so far - LanguageModelConsistencyInfo consistency_info; // path consistency info - AssociateStats associate_stats; // character widths/gaps/seams - - // Flags for marking the entry as a top choice path with - // the smallest rating or lower/upper case letters). - LanguageModelFlagsType top_choice_flags; - - // Extra information maintained by Dawg laguage model component - // (owned by ViterbiStateEntry). - LanguageModelDawgInfo *dawg_info; - - // Extra information maintained by Ngram laguage model component - // (owned by ViterbiStateEntry). - LanguageModelNgramInfo *ngram_info; - - bool updated; // set to true if the entry has just been created/updated -}; - -ELISTIZEH(ViterbiStateEntry); - -// Struct to store information maintained by various language model components. -struct LanguageModelState { - LanguageModelState(int col, int row) : contained_in_col(col), - contained_in_row(row), viterbi_state_entries_prunable_length(0), - viterbi_state_entries_length(0), - viterbi_state_entries_prunable_max_cost(MAX_FLOAT32) {} - ~LanguageModelState() {} - - // Ratings matrix cell that holds this LanguageModelState - // (needed to construct best STATE for rebuild_current_state() - // and best BLOB_CHOICE_LIST_VECTOR for AcceptableChoice()). - int contained_in_col; - int contained_in_row; - - // Storage for the Viterbi state. - ViterbiStateEntry_LIST viterbi_state_entries; - // Number and max cost of prunable paths in viterbi_state_entries. - int viterbi_state_entries_prunable_length; - // Total number of entries in viterbi_state_entries. - int viterbi_state_entries_length; - float viterbi_state_entries_prunable_max_cost; - - // TODO(daria): add font consistency checking. -}; - -// Bundle together all the things pertaining to the best choice/state. -struct BestChoiceBundle { - BestChoiceBundle(STATE *s, WERD_CHOICE *bc, WERD_CHOICE *rc, - BLOB_CHOICE_LIST_VECTOR *bcc) - : best_state(s), best_choice(bc), raw_choice(rc), - best_char_choices(bcc), updated(false), best_vse(NULL), best_b(NULL) {} - - STATE *best_state; - WERD_CHOICE *best_choice; - WERD_CHOICE *raw_choice; - BLOB_CHOICE_LIST_VECTOR *best_char_choices; - bool updated; - DANGERR fixpt; - ViterbiStateEntry *best_vse; // best ViterbiStateEntry and BLOB_CHOICE - BLOB_CHOICE *best_b; // at the end of the best choice path -}; - -struct BestPathByColumn { - float avg_cost; - ViterbiStateEntry *best_vse; - BLOB_CHOICE *best_b; -}; - // This class that contains the data structures and functions necessary // to represent and use the knowledge about the language. class LanguageModel { public: - // Adjustments to pain point priority. - static const float kInitialPainPointPriorityAdjustment; - static const float kDefaultPainPointPriorityAdjustment; - static const float kBestChoicePainPointPriorityAdjustment; - static const float kCriticalPainPointPriorityAdjustment; + // Masks for keeping track of top choices that should not be pruned out. + static const LanguageModelFlagsType kSmallestRatingFlag = 0x1; + static const LanguageModelFlagsType kLowerCaseFlag = 0x2; + static const LanguageModelFlagsType kUpperCaseFlag = 0x4; + static const LanguageModelFlagsType kDigitFlag = 0x8; + static const LanguageModelFlagsType kXhtConsistentFlag = 0x10; // Denominator for normalizing per-letter ngram cost when deriving // penalty adjustments. static const float kMaxAvgNgramCost; - // Minimum word length for fixed length dawgs. - // TODO(daria): check in the new chi/jpn.traineddata without the - // fixed length dawg of length 1 and delete this variable. - static const int kMinFixedLengthDawgLength; - // If there is a significant drop in character ngram probability or a - // dangerous ambiguity make the thresholds on what blob combinations - // can be classified looser. - static const float kLooseMaxCharWhRatio; - - // Masks for interpreting which language model components - // were changed by the call to UpdateState(). - static const LanguageModelFlagsType kSmallestRatingFlag = 0x1; - static const LanguageModelFlagsType kLowerCaseFlag = 0x2; - static const LanguageModelFlagsType kUpperCaseFlag = 0x4; - static const LanguageModelFlagsType kConsistentFlag = 0x8; - static const LanguageModelFlagsType kDawgFlag = 0x10; - static const LanguageModelFlagsType kNgramFlag = 0x20; - static const LanguageModelFlagsType kJustClassifiedFlag = 0x80; - static const LanguageModelFlagsType kAllChangedFlag = 0xff; LanguageModel(const UnicityTable *fontinfo_table, Dict *dict); ~LanguageModel(); + // Fills the given floats array with features extracted from path represented + // by the given ViterbiStateEntry. See ccstruct/params_training_featdef.h + // for feature information. + // Note: the function assumes that features points to an array of size + // PTRAIN_NUM_FEATURE_TYPES. + static void ExtractFeaturesFromPath(const ViterbiStateEntry &vse, + float features[]); + // Updates data structures that are used for the duration of the segmentation // search on the current word; void InitForWord(const WERD_CHOICE *prev_word, - bool fixed_pitch, float best_choice_cert, - float max_char_wh_ratio, float rating_cert_scale, - HEAP *pain_points, CHUNKS_RECORD *chunks_record, - BlamerBundle *blamer_bundle, bool debug_blamer); - // Resets all the "updated" flags used by the Viterbi search that were - // "registered" during the update of the ratings matrix. - void CleanUp(); - // Deletes and sets to NULL language model states of each of the - // BLOB_CHOICEs in the given BLOB_CHOICE_LIST. - void DeleteState(BLOB_CHOICE_LIST *choices); + bool fixed_pitch, float max_char_wh_ratio, + float rating_cert_scale); // Updates language model state of the given BLOB_CHOICE_LIST (from // the ratings matrix) a its parent. Updates pain_points if new @@ -321,119 +81,23 @@ class LanguageModel { // of the list. // The list ordered by cost that is computed collectively by several // language model components (currently dawg and ngram components). - // - // best_path_by_column records the lowest cost path found so far for each - // column of the chunks_record->ratings matrix over all the rows. This - // array is updated if a lower cost ViterbiStateEntry is created in curr_col. - LanguageModelFlagsType UpdateState( - LanguageModelFlagsType changed, + bool UpdateState( + bool just_classified, int curr_col, int curr_row, BLOB_CHOICE_LIST *curr_list, - BLOB_CHOICE_LIST *parent_list, - HEAP *pain_points, - BestPathByColumn *best_path_by_column[], - CHUNKS_RECORD *chunks_record, + LanguageModelState *parent_node, + LMPainPoints *pain_points, + WERD_RES *word_res, BestChoiceBundle *best_choice_bundle, BlamerBundle *blamer_bundle); - // Generates pain points from the problematic top choice paths when the - // segmentation search is guided by the character ngram model. - // It is necessary to consider problematic the top choice paths instead of - // the problematic lowest cost paths because the character ngram model - // might assign a very high cost to very improbably paths. For example, - // "liot" might have a much lower cost than "llot", and the character ngram - // model might detect a dip in probability for p(t|lio) at the end of the - // word, but not at the beginning (p(i|l) would be ok). However, looking at - // the dips in character ngram probability of the top choices we would be - // able to stop the problematic points (p(l| l) would be low). - void GenerateNgramModelPainPointsFromColumn(int col, int row, - HEAP *pain_points, - CHUNKS_RECORD *chunks_record); - - // Generates pain points from the problematic lowest cost paths that are - // "promising" (i.e. would have the cost lower than the one recorded in - // best_path_by_column if the problematic ending of the path is removed - // and after being combined with another blob the certainty of the last - // blob is improved). - void GenerateProblematicPathPainPointsFromColumn( - int col, int row, float best_choice_cert, - HEAP *pain_points, BestPathByColumn *best_path_by_column[], - CHUNKS_RECORD *chunks_record); - - // This function can be called after processing column col of the - // chunks_record->ratings matrix in order to find the promising paths - // that were terminated or made inconsistent by the character choices - // in column col. If such paths are identified, this function generates - // pain points to combine the problematic cells of the matrix. - void GeneratePainPointsFromColumn( - int col, - const GenericVector &non_empty_rows, - float best_choice_cert, - HEAP *pain_points, - BestPathByColumn *best_path_by_column[], - CHUNKS_RECORD *chunks_record); - - // Generates a pain point for each problematic point on the best choice - // path. Such problematic points could be a termination of a dicionary - // word, dip in ngram probability, invalid punctuation, inconsistent - // case/chartype/script or punctuation in the middle of a word. - void GeneratePainPointsFromBestChoice( - HEAP *pain_points, - CHUNKS_RECORD *chunks_record, - BestChoiceBundle *best_choice_bundle); - - // Adds a pain point to the given pain_points queue that will cause - // the entry at chunks_record->ratings(col, row) to be classified. - // The priority of the pain point is set to be: - // - // priority_adjustment * sqrt(avg_parent_cost) - // ---------------------------------------------------- - // sqrt(dict_parent_path_length) * |worst_piece_cert| - // - // The priority is further lowered if fragmented is true. - // Reurns true if a new pain point was added to pain_points. - bool GeneratePainPoint(int col, int row, bool ok_to_extend, - float priority_adjustment, - float worst_piece_cert, - bool fragmented, - float best_choice_cert, - float max_char_wh_ratio, - BLOB_CHOICE *parent_b, - ViterbiStateEntry *parent_vse, - CHUNKS_RECORD *chunks_record, - HEAP *pain_points); - // Returns true if an acceptable best choice was discovered. inline bool AcceptableChoiceFound() { return acceptable_choice_found_; } - - // Fills cert with the worst certainty of the top non-fragmented choice - // of the left and right neighbor of the given col,row. - // Sets fragmented if any of the neighbors have a fragmented character - // as the top choice. - inline void GetWorstPieceCertainty(int col, int row, MATRIX *ratings, - float *cert, bool *fragmented) { - *cert = 0.0f; - *fragmented = false; - if (row > 0) { - GetPieceCertainty(ratings->get(col, row-1), cert, fragmented); - } - if (col+1 < ratings->dimension()) { - GetPieceCertainty(ratings->get(col+1, row), cert, fragmented); - } - ASSERT_HOST(*cert < 0.0f); - } - - // Returns outline length of the given blob is computed as: - // rating_cert_scale * rating / certainty - // Since from Wordrec::SegSearch() in segsearch.cpp - // rating_cert_scale = -1.0 * getDict().certainty_scale / rating_scale - // And from Classify::ConvertMatchesToChoices() in adaptmatch.cpp - // Rating = Certainty = next.rating - // Rating *= rating_scale * Results->BlobLength - // Certainty *= -(getDict().certainty_scale) - inline float ComputeOutlineLength(BLOB_CHOICE *b) { - return rating_cert_scale_ * b->rating() / b->certainty(); + inline void SetAcceptableChoiceFound(bool val) { + acceptable_choice_found_ = val; } + // Returns the reference to ParamsModel. + inline ParamsModel &getParamsModel() { return params_model_; } protected: @@ -449,39 +113,6 @@ class LanguageModel { } } - inline bool NonAlphaOrDigitMiddle(int col, int row, int dimension, - UNICHAR_ID unichar_id) { - return (!dict_->getUnicharset().get_isalpha(unichar_id) && - !dict_->getUnicharset().get_isdigit(unichar_id) && - col > 0 && row+1 < dimension); - } - - inline bool IsFragment(BLOB_CHOICE *b) { - return dict_->getUnicharset().get_fragment(b->unichar_id()); - } - - inline bool IsHan(int script_id) { - return ((dict_->getUnicharset().han_sid() != - dict_->getUnicharset().null_sid()) && - (script_id == dict_->getUnicharset().han_sid())); - } - - // Finds the first non-fragmented character in the given BLOB_CHOICE_LIST - // and updates cert if its certainty is less than the one recorded in cert. - // Sets fragmented if the first choice in BLOB_CHOICE_LIST is a fragment. - inline void GetPieceCertainty(BLOB_CHOICE_LIST *blist, - float *cert, bool *fragmented) { - if (blist == NOT_CLASSIFIED || blist->empty()) return; - BLOB_CHOICE_IT bit(blist); - while (!bit.at_last() && IsFragment(bit.data())) { - *fragmented = true; - bit.forward(); // skip fragments - } - // Each classification must have at least one non-fragmented choice. - ASSERT_HOST(!IsFragment(bit.data())); - if (bit.data()->certainty() < *cert) *cert = bit.data()->certainty(); - } - inline float ComputeAdjustment(int num_problems, float penalty) { if (num_problems == 0) return 0.0f; if (num_problems == 1) return penalty; @@ -495,7 +126,7 @@ class LanguageModel { // of inconsistencies on the path. inline float ComputeConsistencyAdjustment( const LanguageModelDawgInfo *dawg_info, - const LanguageModelConsistencyInfo &consistency_info) { + const LMConsistencyInfo &consistency_info) { if (dawg_info != NULL) { return ComputeAdjustment(consistency_info.NumInconsistentCase(), language_model_penalty_case); @@ -514,90 +145,65 @@ class LanguageModel { language_model_penalty_font : 0.0f)); } - // Returns an adjusted ratings sum that includes inconsistency penalties. - inline float ComputeConsistencyAdjustedRatingsSum( - float ratings_sum, - const LanguageModelDawgInfo *dawg_info, - const LanguageModelConsistencyInfo &consistency_info) { - return (ratings_sum * (1.0f + ComputeConsistencyAdjustment( - dawg_info, consistency_info))); - } - // Returns an adjusted ratings sum that includes inconsistency penalties, // penalties for non-dictionary paths and paths with dips in ngram // probability. - float ComputeAdjustedPathCost( - float ratings_sum, int length, float dawg_score, - const LanguageModelDawgInfo *dawg_info, - const LanguageModelNgramInfo *ngram_info, - const LanguageModelConsistencyInfo &consistency_info, - const AssociateStats &associate_stats, - ViterbiStateEntry *parent_vse); - - // Returns true if the given ViterbiStateEntry represents a problematic - // path. A path is considered problematic if the last unichar makes it - // inconsistent, introduces a dip in ngram probability or transforms a - // dictionary path into a non-dictionary one. - bool ProblematicPath(const ViterbiStateEntry &vse, - UNICHAR_ID unichar_id, bool word_end); - - // Finds the first lower and upper case character in curr_list. - // If none found, chooses the first character in the list. - void GetTopChoiceLowerUpper(LanguageModelFlagsType changed, - BLOB_CHOICE_LIST *curr_list, - BLOB_CHOICE **first_lower, - BLOB_CHOICE **first_upper); - + float ComputeAdjustedPathCost(ViterbiStateEntry *vse); + + // Finds the first lower and upper case letter and first digit in curr_list. + // Uses the first character in the list in place of empty results. + // Returns true if both alpha and digits are found. + bool GetTopLowerUpperDigit(BLOB_CHOICE_LIST *curr_list, + BLOB_CHOICE **first_lower, + BLOB_CHOICE **first_upper, + BLOB_CHOICE **first_digit) const; + // Forces there to be at least one entry in the overall set of the + // viterbi_state_entries of each element of parent_node that has the + // top_choice_flag set for lower, upper and digit using the same rules as + // GetTopLowerUpperDigit, setting the flag on the first found suitable + // candidate, whether or not the flag is set on some other parent. + // Returns 1 if both alpha and digits are found among the parents, -1 if no + // parents are found at all (a legitimate case), and 0 otherwise. + int SetTopParentLowerUpperDigit(LanguageModelState *parent_node) const; + + // Finds the next ViterbiStateEntry with which the given unichar_id can + // combine sensibly, taking into account any mixed alnum/mixed case + // situation, and whether this combination has been inspected before. + ViterbiStateEntry* GetNextParentVSE( + bool just_classified, bool mixed_alnum, + const BLOB_CHOICE* bc, LanguageModelFlagsType blob_choice_flags, + const UNICHARSET& unicharset, WERD_RES* word_res, + ViterbiStateEntry_IT* vse_it, + LanguageModelFlagsType* top_choice_flags) const; // Helper function that computes the cost of the path composed of the // path in the given parent ViterbiStateEntry and the given BLOB_CHOICE. - // Adds a new ViterbiStateEntry to the list of viterbi entries - // in the given BLOB_CHOICE if the new path looks good enough. - // Returns LanguageModelFlagsType that indicates which language - // model components were involved in creating the new entry. - LanguageModelFlagsType AddViterbiStateEntry( - LanguageModelFlagsType top_choice_flags, - float denom, - bool word_end, - int curr_col, int curr_row, - BLOB_CHOICE *b, - BLOB_CHOICE *parent_b, - ViterbiStateEntry *parent_vse, - HEAP *pain_points, - BestPathByColumn *best_path_by_column[], - CHUNKS_RECORD *chunks_record, - BestChoiceBundle *best_choice_bundle, - BlamerBundle *blamer_bundle); - - // Pretty print information in the given ViterbiStateEntry. - void PrintViterbiStateEntry(const char *msg, - ViterbiStateEntry *vse, - BLOB_CHOICE *b, - CHUNKS_RECORD *chunks_record); + // If the new path looks good enough, adds a new ViterbiStateEntry to the + // list of viterbi entries in the given BLOB_CHOICE and returns true. + bool AddViterbiStateEntry( + LanguageModelFlagsType top_choice_flags, float denom, bool word_end, + int curr_col, int curr_row, BLOB_CHOICE *b, + LanguageModelState *curr_state, ViterbiStateEntry *parent_vse, + LMPainPoints *pain_points, WERD_RES *word_res, + BestChoiceBundle *best_choice_bundle, BlamerBundle *blamer_bundle); // Determines whether a potential entry is a true top choice and // updates changed accordingly. // // Note: The function assumes that b, top_choice_flags and changed // are not NULL. - void GenerateTopChoiceInfo( - float ratings_sum, - const LanguageModelDawgInfo *dawg_info, - const LanguageModelConsistencyInfo &consistency_info, - const ViterbiStateEntry *parent_vse, - BLOB_CHOICE *b, - LanguageModelFlagsType *top_choice_flags, - LanguageModelFlagsType *changed); + void GenerateTopChoiceInfo(ViterbiStateEntry *new_vse, + const ViterbiStateEntry *parent_vse, + LanguageModelState *lms); // Calls dict_->LetterIsOk() with DawgArgs initialized from parent_vse and // unichar from b.unichar_id(). Constructs and returns LanguageModelDawgInfo // with updated active dawgs, constraints and permuter. // // Note: the caller is responsible for deleting the returned pointer. - LanguageModelDawgInfo *GenerateDawgInfo(bool word_end, int script_id, + LanguageModelDawgInfo *GenerateDawgInfo(bool word_end, int curr_col, int curr_row, const BLOB_CHOICE &b, - const ViterbiStateEntry *parent_vse, - LanguageModelFlagsType *changed); + const ViterbiStateEntry *parent_vse); // Computes p(unichar | parent context) and records it in ngram_cost. // If b.unichar_id() is an unlikely continuation of the parent context @@ -606,12 +212,10 @@ class LanguageModel { // updated context (that includes b.unichar_id() at the end) and returns it. // // Note: the caller is responsible for deleting the returned pointer. - LanguageModelNgramInfo *GenerateNgramInfo(const char *unichar, - float certainty, float denom, - int curr_col, int curr_row, - const ViterbiStateEntry *parent_vse, - BLOB_CHOICE *parent_b, - LanguageModelFlagsType *changed); + LanguageModelNgramInfo *GenerateNgramInfo( + const char *unichar, float certainty, float denom, + int curr_col, int curr_row, float outline_length, + const ViterbiStateEntry *parent_vse); // Computes -(log(prob(classifier)) + log(prob(ngram model))) // for the given unichar in the given context. If there are multiple @@ -632,9 +236,9 @@ class LanguageModel { // and on the consistency of the given unichar_id with parent_vse. void FillConsistencyInfo( int curr_col, bool word_end, BLOB_CHOICE *b, - ViterbiStateEntry *parent_vse, BLOB_CHOICE *parent_b, - CHUNKS_RECORD *chunks_record, - LanguageModelConsistencyInfo *consistency_info); + ViterbiStateEntry *parent_vse, + WERD_RES *word_res, + LMConsistencyInfo *consistency_info); // Constructs WERD_CHOICE by recording unichar_ids of the BLOB_CHOICEs // on the path represented by the given BLOB_CHOICE and language model @@ -643,87 +247,51 @@ class LanguageModel { // constructed WERD_CHOICE is better than the best/raw choice recorded // in the best_choice_bundle, this function updates the corresponding // fields and sets best_choice_bunldle->updated to true. - void UpdateBestChoice(BLOB_CHOICE *b, - ViterbiStateEntry *vse, - HEAP *pain_points, - CHUNKS_RECORD *chunks_record, + void UpdateBestChoice(ViterbiStateEntry *vse, + LMPainPoints *pain_points, + WERD_RES *word_res, BestChoiceBundle *best_choice_bundle, BlamerBundle *blamer_bundle); - // Fills the given floats array with raw features extracted from the - // path represented by the given ViterbiStateEntry. - // See ccstruct/params_training_featdef.h for feature information. - void ExtractRawFeaturesFromPath(const ViterbiStateEntry &vse, - float *features); - // Constructs a WERD_CHOICE by tracing parent pointers starting with // the given LanguageModelStateEntry. Returns the constructed word. // Updates best_char_choices, certainties and state if they are not // NULL (best_char_choices and certainties are assumed to have the // length equal to lmse->length). - // The caller is resposible for freeing memory associated with the + // The caller is responsible for freeing memory associated with the // returned WERD_CHOICE. - WERD_CHOICE *ConstructWord(BLOB_CHOICE *b, - ViterbiStateEntry *vse, - CHUNKS_RECORD *chunks_record, - BLOB_CHOICE_LIST_VECTOR *best_char_choices, - float certainties[], - float *dawg_score, - STATE *state, + WERD_CHOICE *ConstructWord(ViterbiStateEntry *vse, + WERD_RES *word_res, + DANGERR *fixpt, BlamerBundle *blamer_bundle, bool *truth_path); - // This function is used for non-space delimited languages when looking - // for word endings recorded while trying to separate the path into words. - // - // The function increments covered if a valid word ending is found in - // active_dawgs (if covered is incremented, skip is set to the number - // of unichars that should be skipped because they are covered by the - // word whose ending was just discovered). - // - // dawg_score and dawg_score_done are updated if: - // -- at the end of the path we discover a valid word ending from a - // non-fixed length dawg (this means that the whole word is a - // valid word, so dawg_score is set to 1.0f - // -- word_start is true (dawg_score is set to covered / word length) - // - // Note: this function assumes that skip, covered, dawg_score and - // dawg_score_done are not NULL. - void UpdateCoveredByFixedLengthDawgs(const DawgInfoVector &active_dawgs, - int word_index, int word_length, - int *skip, int *covered, - float *dawg_score, - bool *dawg_score_done); - // Wrapper around AssociateUtils::ComputeStats(). inline void ComputeAssociateStats(int col, int row, float max_char_wh_ratio, ViterbiStateEntry *parent_vse, - CHUNKS_RECORD *chunks_record, + WERD_RES *word_res, AssociateStats *associate_stats) { - AssociateUtils::ComputeStats( - col, row, - (parent_vse != NULL) ? &(parent_vse->associate_stats) : NULL, - (parent_vse != NULL) ? parent_vse->length : 0, - fixed_pitch_, max_char_wh_ratio, - chunks_record->word_res != NULL ? &chunks_record->word_res->denorm : NULL, - chunks_record, language_model_debug_level, associate_stats); + AssociateUtils::ComputeStats( + col, row, + (parent_vse != NULL) ? &(parent_vse->associate_stats) : NULL, + (parent_vse != NULL) ? parent_vse->length : 0, + fixed_pitch_, max_char_wh_ratio, + word_res, language_model_debug_level > 2, associate_stats); } // Returns true if the path with such top_choice_flags and dawg_info // could be pruned out (i.e. is neither a system/user/frequent dictionary // nor a top choice path). // In non-space delimited languages all paths can be "somewhat" dictionary - // words. In such languages we can not do dictionary-driven path prunning, + // words. In such languages we can not do dictionary-driven path pruning, // so paths with non-empty dawg_info are considered prunable. - inline bool PrunablePath(LanguageModelFlagsType top_choice_flags, - const LanguageModelDawgInfo *dawg_info) { - if (top_choice_flags) return false; - if (dawg_info != NULL && - (dawg_info->permuter == SYSTEM_DAWG_PERM || - dawg_info->permuter == USER_DAWG_PERM || - dawg_info->permuter == FREQ_DAWG_PERM) && - dict_->GetMaxFixedLengthDawgIndex() < 0) return false; + inline bool PrunablePath(const ViterbiStateEntry &vse) { + if (vse.top_choice_flags) return false; + if (vse.dawg_info != NULL && + (vse.dawg_info->permuter == SYSTEM_DAWG_PERM || + vse.dawg_info->permuter == USER_DAWG_PERM || + vse.dawg_info->permuter == FREQ_DAWG_PERM)) return false; return true; } @@ -741,8 +309,8 @@ class LanguageModel { INT_VAR_H(language_model_ngram_order, 8, "Maximum order of the character ngram model"); INT_VAR_H(language_model_viterbi_list_max_num_prunable, 10, - "Maximum number of prunable (those for which PrunablePath() is true)" - "entries in each viterbi list recorded in BLOB_CHOICEs"); + "Maximum number of prunable (those for which PrunablePath() is" + " true) entries in each viterbi list recorded in BLOB_CHOICEs"); INT_VAR_H(language_model_viterbi_list_max_size, 500, "Maximum size of viterbi lists recorded in BLOB_CHOICEs"); double_VAR_H(language_model_ngram_small_prob, 0.000001, @@ -756,14 +324,13 @@ class LanguageModel { double_VAR_H(language_model_ngram_scale_factor, 0.03, "Strength of the character ngram model relative to the" " character classifier "); + double_VAR_H(language_model_ngram_rating_factor, 16.0, + "Factor to bring log-probs into the same range as ratings" + " when multiplied by outline length "); BOOL_VAR_H(language_model_ngram_space_delimited_language, true, "Words are delimited by space"); - INT_VAR_H(language_model_min_compound_length, 3, "Minimum length of compound words"); - INT_VAR_H(language_model_fixed_length_choices_depth, 3, - "Depth of blob choice lists to explore" - " when fixed length dawgs are on"); // Penalties used for adjusting path costs and final word rating. double_VAR_H(language_model_penalty_non_freq_dict_word, 0.1, "Penalty for words not in the frequent word dictionary"); @@ -782,18 +349,17 @@ class LanguageModel { double_VAR_H(language_model_penalty_spacing, 0.05, "Penalty for inconsistent spacing"); double_VAR_H(language_model_penalty_increment, 0.01, "Penalty increment"); + INT_VAR_H(wordrec_display_segmentations, 0, "Display Segmentations"); BOOL_VAR_H(language_model_use_sigmoidal_certainty, false, "Use sigmoidal score for certainty"); + protected: // Member Variables. // Temporary DawgArgs struct that is re-used across different words to // avoid dynamic memory re-allocation (should be cleared before each use). DawgArgs *dawg_args_; - // List of pointers to updated flags used by Viterbi search to mark - // recently updated ViterbiStateEntries. - GenericVector updated_flags_; // Scaling for recovering blob outline length from rating and certainty. float rating_cert_scale_; @@ -823,13 +389,9 @@ class LanguageModel { // only the last language_model_ngram_order of the word are stored). STRING prev_word_str_; int prev_word_unichar_step_len_; - // Active dawg and constraints vector. - DawgInfoVector *beginning_active_dawgs_; - DawgInfoVector *beginning_constraints_; - DawgInfoVector *fixed_length_beginning_active_dawgs_; - DawgInfoVector *empty_dawg_info_vec_; - // Maximum adjustment factor for character ngram choices. - float max_penalty_adjust_; + // Active dawg vector. + DawgPositionVector *very_beginning_active_dawgs_; // includes continuation + DawgPositionVector *beginning_active_dawgs_; // Set to true if acceptable choice was discovered. // Note: it would be nice to use this to terminate the search once an // acceptable choices is found. However we do not do that and once an @@ -845,6 +407,8 @@ class LanguageModel { // Set to true if a choice representing correct segmentation was explored. bool correct_segmentation_explored_; + // Params models containing weights for for computing ViterbiStateEntry costs. + ParamsModel params_model_; }; } // namespace tesseract diff --git a/wordrec/lm_consistency.cpp b/wordrec/lm_consistency.cpp new file mode 100644 index 0000000000..48c3661848 --- /dev/null +++ b/wordrec/lm_consistency.cpp @@ -0,0 +1,113 @@ +/////////////////////////////////////////////////////////////////////// +// File: lm_consistency.cpp +// Description: Struct for recording consistency of the paths representing +// OCR hypotheses. +// Author: Rika Antonova +// Created: Mon Jun 20 11:26:43 PST 2012 +// +// (C) Copyright 2012, Google Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////// + +#include "lm_consistency.h" + +#include "associate.h" +#include "dict.h" +#include "ratngs.h" + +namespace tesseract { + +void LMConsistencyInfo::ComputeXheightConsistency( + const BLOB_CHOICE *b, bool is_punc) { + if (xht_decision == XH_INCONSISTENT) + return; // It isn't going to get any better. + + // Compute xheight consistency. + bool parent_null = xht_sp < 0; + int parent_sp = xht_sp; + // Debug strings. + if (b->yshift() > LMConsistencyInfo::kShiftThresh) { + xht_sp = LMConsistencyInfo::kSUP; + } else if (b->yshift() < -LMConsistencyInfo::kShiftThresh) { + xht_sp = LMConsistencyInfo::kSUB; + } else { + xht_sp = LMConsistencyInfo::kNORM; + } + xht_count[xht_sp]++; + if (is_punc) xht_count_punc[xht_sp]++; + if (!parent_null) { + xpos_entropy += abs(parent_sp - xht_sp); + } + // TODO(eger): Figure out a better way to account for small caps. + // For the first character not y-shifted, we only care if it is too small. + // Too large is common in drop caps and small caps. + // inT16 small_xht = b->min_xheight(); + // if (parent_vse == NULL && sp == LanguageModelConsistencyInfo::kNORM) { + // small_xht = 0; + // } + IntersectRange(b->min_xheight(), b->max_xheight(), + &(xht_lo[xht_sp]), &(xht_hi[xht_sp])); + + + // Compute xheight inconsistency kinds. + if (parent_null) { + if (xht_count[kNORM] == 1) { + xht_decision = XH_GOOD; + } else { + xht_decision = XH_SUBNORMAL; + } + return; + } + + // When we intersect the ranges of xheights in pixels for all characters in + // each position (subscript, normal, superscript), + // How much range must be left? 0? [exactly one pixel height for xheight] 1? + // TODO(eger): Extend this code to take a prior for the rest of the line. + const int kMinIntersectedXHeightRange = 0; + for (int i = 0; i < kNumPos; i++) { + if (xht_lo[i] > xht_hi[i] - kMinIntersectedXHeightRange) { + xht_decision = XH_INCONSISTENT; + return; + } + } + + // Reject as improbable anything where there's much punctuation in subscript + // or superscript regions. + if (xht_count_punc[kSUB] > xht_count[kSUB] * 0.4 || + xht_count_punc[kSUP] > xht_count[kSUP] * 0.4) { + xht_decision = XH_INCONSISTENT; + return; + } + + // Now check that the subscript and superscript aren't too small relative to + // the mainline. + double mainline_xht = static_cast(xht_lo[kNORM]); + double kMinSizeRatio = 0.4; + if (mainline_xht > 0.0 && + (static_cast(xht_hi[kSUB]) / mainline_xht < kMinSizeRatio || + static_cast(xht_hi[kSUP]) / mainline_xht < kMinSizeRatio)) { + xht_decision = XH_INCONSISTENT; + return; + } + // TODO(eger): Check into inconsistency of super/subscript y offsets. + if (xpos_entropy > kMaxEntropy) { + xht_decision = XH_INCONSISTENT; + return; + } + if (xht_count[kSUB] == 0 && xht_count[kSUP] == 0) { + xht_decision = XH_GOOD; + return; + } + xht_decision = XH_SUBNORMAL; +} + +} // namespace tesseract diff --git a/wordrec/lm_consistency.h b/wordrec/lm_consistency.h new file mode 100644 index 0000000000..1d452157a0 --- /dev/null +++ b/wordrec/lm_consistency.h @@ -0,0 +1,143 @@ +/////////////////////////////////////////////////////////////////////// +// File: lm_consistency.h +// Description: Struct for recording consistency of the paths representing +// OCR hypotheses. +// Author: Rika Antonova +// Created: Mon Jun 20 11:26:43 PST 2012 +// +// (C) Copyright 2012, Google Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////// + +#include "dawg.h" +#include "dict.h" +#include "host.h" +#include "ratngs.h" + +#ifndef TESSERACT_WORDREC_CONSISTENCY_H_ +#define TESSERACT_WORDREC_CONSISTENCY_H_ + +namespace tesseract { + +static const char * const XHeightConsistencyEnumName[] = { + "XH_GOOD", + "XH_SUBNORMAL", + "XH_INCONSISTENT", +}; + +// Struct for keeping track of the consistency of the path. +struct LMConsistencyInfo { + enum ChartypeEnum { CT_NONE, CT_ALPHA, CT_DIGIT, CT_OTHER}; + + // How much do characters have to be shifted away from normal parameters + // before we say they're not normal? + static const int kShiftThresh = 1; + + // How much shifting from subscript to superscript and back + // before we declare shenanigans? + static const int kMaxEntropy = 1; + + // Script positions - order important for entropy calculation. + static const int kSUB = 0, kNORM = 1, kSUP = 2; + static const int kNumPos = 3; + + explicit LMConsistencyInfo(const LMConsistencyInfo* parent_info) { + if (parent_info == NULL) { + // Initialize from scratch. + num_alphas = 0; + num_digits = 0; + num_punc = 0; + num_other = 0; + chartype = CT_NONE; + punc_ref = NO_EDGE; + invalid_punc = false; + num_non_first_upper = 0; + num_lower = 0; + script_id = 0; + inconsistent_script = false; + num_inconsistent_spaces = 0; + inconsistent_font = false; + // Initialize XHeight stats. + for (int i = 0; i < kNumPos; i++) { + xht_count[i] = 0; + xht_count_punc[i] = 0; + xht_lo[i] = 0; + xht_hi[i] = 256; // kBlnCellHeight + } + xht_sp = -1; // This invalid value indicates that there was no parent. + xpos_entropy = 0; + xht_decision = XH_GOOD; + } else { + // Copy parent info + *this = *parent_info; + } + } + inline int NumInconsistentPunc() const { + return invalid_punc ? num_punc : 0; + } + inline int NumInconsistentCase() const { + return (num_non_first_upper > num_lower) ? num_lower : num_non_first_upper; + } + inline int NumInconsistentChartype() const { + return (NumInconsistentPunc() + num_other + + ((num_alphas > num_digits) ? num_digits : num_alphas)); + } + inline bool Consistent() const { + return (NumInconsistentPunc() == 0 && NumInconsistentCase() == 0 && + NumInconsistentChartype() == 0 && !inconsistent_script && + !inconsistent_font && !InconsistentXHeight()); + } + inline int NumInconsistentSpaces() const { + return num_inconsistent_spaces; + } + inline int InconsistentXHeight() const { + return xht_decision == XH_INCONSISTENT; + } + void ComputeXheightConsistency(const BLOB_CHOICE *b, bool is_punc); + float BodyMinXHeight() const { + if (InconsistentXHeight()) + return 0.0f; + return xht_lo[kNORM]; + } + float BodyMaxXHeight() const { + if (InconsistentXHeight()) + return static_cast(MAX_INT16); + return xht_hi[kNORM]; + } + + int num_alphas; + int num_digits; + int num_punc; + int num_other; + ChartypeEnum chartype; + EDGE_REF punc_ref; + bool invalid_punc; + int num_non_first_upper; + int num_lower; + int script_id; + bool inconsistent_script; + int num_inconsistent_spaces; + bool inconsistent_font; + // Metrics clumped by position. + float xht_lo[kNumPos]; + float xht_hi[kNumPos]; + inT16 xht_count[kNumPos]; + inT16 xht_count_punc[kNumPos]; + inT16 xht_sp; + inT16 xpos_entropy; + XHeightConsistencyEnum xht_decision; +}; + + +} // namespace tesseract + +#endif // TESSERACT_WORDREC_CONSISTENCY_H_ diff --git a/wordrec/lm_pain_points.cpp b/wordrec/lm_pain_points.cpp new file mode 100644 index 0000000000..1885088775 --- /dev/null +++ b/wordrec/lm_pain_points.cpp @@ -0,0 +1,216 @@ +/////////////////////////////////////////////////////////////////////// +// File: pain_points.cpp +// Description: Functions that utilize the knowledge about the properties +// of the paths explored by the segmentation search in order +// to "pain points" - the locations in the ratings matrix +// which should be classified next. +// Author: Rika Antonova +// Created: Mon Jun 20 11:26:43 PST 2012 +// +// (C) Copyright 2012, Google Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +/////////////////////////////////////////////////////////////////////// + +#include "lm_pain_points.h" + +#include "associate.h" +#include "dict.h" +#include "genericheap.h" +#include "lm_state.h" +#include "matrix.h" +#include "pageres.h" + +namespace tesseract { + +const float LMPainPoints::kDefaultPainPointPriorityAdjustment = 2.0f; +const float LMPainPoints::kLooseMaxCharWhRatio = 2.5f; + +LMPainPointsType LMPainPoints::Deque(MATRIX_COORD *pp, float *priority) { + for (int h = 0; h < LM_PPTYPE_NUM; ++h) { + if (pain_points_heaps_[h].empty()) continue; + *priority = pain_points_heaps_[h].PeekTop().key; + *pp = pain_points_heaps_[h].PeekTop().data; + pain_points_heaps_[h].Pop(NULL); + return static_cast(h); + } + return LM_PPTYPE_NUM; +} + +void LMPainPoints::GenerateInitial(WERD_RES *word_res) { + MATRIX *ratings = word_res->ratings; + AssociateStats associate_stats; + for (int col = 0; col < ratings->dimension(); ++col) { + int row_end = MIN(ratings->dimension(), col + ratings->bandwidth() + 1); + for (int row = col + 1; row < row_end; ++row) { + MATRIX_COORD coord(col, row); + if (coord.Valid(*ratings) && + ratings->get(col, row) != NOT_CLASSIFIED) continue; + // Add an initial pain point if needed. + if (ratings->Classified(col, row - 1, dict_->WildcardID()) || + (col + 1 < ratings->dimension() && + ratings->Classified(col + 1, row, dict_->WildcardID()))) { + GeneratePainPoint(col, row, LM_PPTYPE_SHAPE, 0.0, + true, max_char_wh_ratio_, word_res); + } + } + } +} + +void LMPainPoints::GenerateFromPath(float rating_cert_scale, + ViterbiStateEntry *vse, + WERD_RES *word_res) { + ViterbiStateEntry *curr_vse = vse; + BLOB_CHOICE *curr_b = vse->curr_b; + // The following pain point generation and priority calculation approaches + // prioritize exploring paths with low average rating of the known part of + // the path, while not relying on the ratings of the pieces to be combined. + // + // A pain point to combine the neighbors is generated for each pair of + // neighboring blobs on the path (the path is represented by vse argument + // given to GenerateFromPath()). The priority of each pain point is set to + // the average rating (per outline length) of the path, not including the + // ratings of the blobs to be combined. + // The ratings of the blobs to be combined are not used to calculate the + // priority, since it is not possible to determine from their magnitude + // whether it will be beneficial to combine the blobs. The reason is that + // chopped junk blobs (/ | - ') can have very good (low) ratings, however + // combining them will be beneficial. Blobs with high ratings might be + // over-joined pieces of characters, but also could be blobs from an unseen + // font or chopped pieces of complex characters. + while (curr_vse->parent_vse != NULL) { + ViterbiStateEntry* parent_vse = curr_vse->parent_vse; + const MATRIX_COORD& curr_cell = curr_b->matrix_cell(); + const MATRIX_COORD& parent_cell = parent_vse->curr_b->matrix_cell(); + MATRIX_COORD pain_coord(parent_cell.col, curr_cell.row); + if (!pain_coord.Valid(*word_res->ratings) || + !word_res->ratings->Classified(parent_cell.col, curr_cell.row, + dict_->WildcardID())) { + // rat_subtr contains ratings sum of the two adjacent blobs to be merged. + // rat_subtr will be subtracted from the ratings sum of the path, since + // the blobs will be joined into a new blob, whose rating is yet unknown. + float rat_subtr = curr_b->rating() + parent_vse->curr_b->rating(); + // ol_subtr contains the outline length of the blobs that will be joined. + float ol_subtr = + AssociateUtils::ComputeOutlineLength(rating_cert_scale, *curr_b) + + AssociateUtils::ComputeOutlineLength(rating_cert_scale, + *(parent_vse->curr_b)); + // ol_dif is the outline of the path without the two blobs to be joined. + float ol_dif = vse->outline_length - ol_subtr; + // priority is set to the average rating of the path per unit of outline, + // not counting the ratings of the pieces to be joined. + float priority = ol_dif > 0 ? (vse->ratings_sum-rat_subtr)/ol_dif : 0.0; + GeneratePainPoint(pain_coord.col, pain_coord.row, LM_PPTYPE_PATH, + priority, true, max_char_wh_ratio_, word_res); + } else if (debug_level_ > 3) { + tprintf("NO pain point (Classified) for col=%d row=%d type=%s\n", + pain_coord.col, pain_coord.row, + LMPainPointsTypeName[LM_PPTYPE_PATH]); + BLOB_CHOICE_IT b_it(word_res->ratings->get(pain_coord.col, + pain_coord.row)); + for (b_it.mark_cycle_pt(); !b_it.cycled_list(); b_it.forward()) { + BLOB_CHOICE* choice = b_it.data(); + choice->print_full(); + } + } + + curr_vse = parent_vse; + curr_b = curr_vse->curr_b; + } +} + +void LMPainPoints::GenerateFromAmbigs(const DANGERR &fixpt, + ViterbiStateEntry *vse, + WERD_RES *word_res) { + // Begins and ends in DANGERR vector now record the blob indices as used + // by the ratings matrix. + for (int d = 0; d < fixpt.size(); ++d) { + const DANGERR_INFO &danger = fixpt[d]; + // Only use dangerous ambiguities. + if (danger.dangerous) { + GeneratePainPoint(danger.begin, danger.end - 1, + LM_PPTYPE_AMBIG, vse->cost, true, + kLooseMaxCharWhRatio, word_res); + } + } +} + +bool LMPainPoints::GeneratePainPoint( + int col, int row, LMPainPointsType pp_type, float special_priority, + bool ok_to_extend, float max_char_wh_ratio, + WERD_RES *word_res) { + MATRIX_COORD coord(col, row); + if (coord.Valid(*word_res->ratings) && + word_res->ratings->Classified(col, row, dict_->WildcardID())) { + return false; + } + if (debug_level_ > 3) { + tprintf("Generating pain point for col=%d row=%d type=%s\n", + col, row, LMPainPointsTypeName[pp_type]); + } + // Compute associate stats. + AssociateStats associate_stats; + AssociateUtils::ComputeStats(col, row, NULL, 0, fixed_pitch_, + max_char_wh_ratio, word_res, debug_level_, + &associate_stats); + // For fixed-pitch fonts/languages: if the current combined blob overlaps + // the next blob on the right and it is ok to extend the blob, try extending + // the blob until there is no overlap with the next blob on the right or + // until the width-to-height ratio becomes too large. + if (ok_to_extend) { + while (associate_stats.bad_fixed_pitch_right_gap && + row + 1 < word_res->ratings->dimension() && + !associate_stats.bad_fixed_pitch_wh_ratio) { + AssociateUtils::ComputeStats(col, ++row, NULL, 0, fixed_pitch_, + max_char_wh_ratio, word_res, debug_level_, + &associate_stats); + } + } + if (associate_stats.bad_shape) { + if (debug_level_ > 3) { + tprintf("Discarded pain point with a bad shape\n"); + } + return false; + } + + // Insert the new pain point into pain_points_heap_. + if (pain_points_heaps_[pp_type].size() < max_heap_size_) { + // Compute pain point priority. + float priority; + if (pp_type == LM_PPTYPE_PATH) { + priority = special_priority; + } else { + priority = associate_stats.gap_sum; + } + MatrixCoordPair pain_point(priority, MATRIX_COORD(col, row)); + pain_points_heaps_[pp_type].Push(&pain_point); + if (debug_level_) { + tprintf("Added pain point with priority %g\n", priority); + } + return true; + } else { + if (debug_level_) tprintf("Pain points heap is full\n"); + return false; + } +} + +// Adjusts the pain point coordinates to cope with expansion of the ratings +// matrix due to a split of the blob with the given index. +void LMPainPoints::RemapForSplit(int index) { + for (int i = 0; i < LM_PPTYPE_NUM; ++i) { + GenericVector* heap = pain_points_heaps_[i].heap(); + for (int j = 0; j < heap->size(); ++j) + (*heap)[j].data.MapForSplit(index); + } +} + +} // namespace tesseract + diff --git a/wordrec/lm_pain_points.h b/wordrec/lm_pain_points.h new file mode 100644 index 0000000000..0f47b1ddd0 --- /dev/null +++ b/wordrec/lm_pain_points.h @@ -0,0 +1,138 @@ +/////////////////////////////////////////////////////////////////////// +// File: lm_pain_points.h +// Description: Functions that utilize the knowledge about the properties +// of the paths explored by the segmentation search in order +// to generate "pain points" - the locations in the ratings +// matrix which should be classified next. +// Author: Rika Antonova +// Created: Mon Jun 20 11:26:43 PST 2012 +// +// (C) Copyright 2012, Google Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +/////////////////////////////////////////////////////////////////////// + +#ifndef TESSERACT_WORDREC_PAIN_POINTS_H_ +#define TESSERACT_WORDREC_PAIN_POINTS_H_ + +#include "associate.h" +#include "dict.h" +#include "genericheap.h" +#include "lm_state.h" + +namespace tesseract { + +// Heap of pain points used for determining where to chop/join. +typedef GenericHeap PainPointHeap; + +// Types of pain points (ordered in the decreasing level of importance). +enum LMPainPointsType { + LM_PPTYPE_BLAMER, + LM_PPTYPE_AMBIG, + LM_PPTYPE_PATH, + LM_PPTYPE_SHAPE, + + LM_PPTYPE_NUM +}; + +static const char * const LMPainPointsTypeName[] = { + "LM_PPTYPE_BLAMER", + "LM_PPTYPE_AMBIGS", + "LM_PPTYPE_PATH", + "LM_PPTYPE_SHAPE", +}; + +class LMPainPoints { + public: + + static const float kDefaultPainPointPriorityAdjustment; + // If there is a significant drop in character ngram probability or a + // dangerous ambiguity make the thresholds on what blob combinations + // can be classified looser. + static const float kLooseMaxCharWhRatio; + // Returns a description of the type of a pain point. + static const char* PainPointDescription(LMPainPointsType type) { + return LMPainPointsTypeName[type]; + } + + LMPainPoints(int max, float rat, bool fp, const Dict *d, int deb) : + max_heap_size_(max), max_char_wh_ratio_(rat), fixed_pitch_(fp), + dict_(d), debug_level_(deb) {} + ~LMPainPoints() {} + + // Returns true if the heap of pain points of pp_type is not empty(). + inline bool HasPainPoints(LMPainPointsType pp_type) const { + return !pain_points_heaps_[pp_type].empty(); + } + + // Dequeues the next pain point from the pain points queue and copies + // its contents and priority to *pp and *priority. + // Returns LM_PPTYPE_NUM if pain points queue is empty, otherwise the type. + LMPainPointsType Deque(MATRIX_COORD *pp, float *priority); + + // Clears pain points heap. + void Clear() { + for (int h = 0; h < LM_PPTYPE_NUM; ++h) pain_points_heaps_[h].clear(); + } + + // For each cell, generate a "pain point" if the cell is not classified + // and has a left or right neighbor that was classified. + void GenerateInitial(WERD_RES *word_res); + + // Generate pain points from the given path. + void GenerateFromPath(float rating_cert_scale, ViterbiStateEntry *vse, + WERD_RES *word_res); + + // Generate pain points from dangerous ambiguities in best choice. + void GenerateFromAmbigs(const DANGERR &fixpt, ViterbiStateEntry *vse, + WERD_RES *word_res); + + // Generate a pain point for the blamer. + bool GenerateForBlamer(double max_char_wh_ratio, WERD_RES *word_res, + int col, int row) { + return GeneratePainPoint(col, row, LM_PPTYPE_BLAMER, 0.0, false, + max_char_wh_ratio, word_res); + } + + // Adds a pain point to classify chunks_record->ratings(col, row). + // Returns true if a new pain point was added to an appropriate heap. + // Pain point priority is set to special_priority for pain points of + // LM_PPTYPE_AMBIG or LM_PPTYPE_PATH, for other pain points + // AssociateStats::gap_sum is used. + bool GeneratePainPoint(int col, int row, LMPainPointsType pp_type, + float special_priority, bool ok_to_extend, + float max_char_wh_ratio, + WERD_RES *word_res); + + // Adjusts the pain point coordinates to cope with expansion of the ratings + // matrix due to a split of the blob with the given index. + void RemapForSplit(int index); + + private: + // Priority queues containing pain points generated by the language model + // The priority is set by the language model components, adjustments like + // seam cost and width priority are factored into the priority. + PainPointHeap pain_points_heaps_[LM_PPTYPE_NUM]; + // Maximum number of points to keep in the heap. + int max_heap_size_; + // Maximum character width/height ratio. + float max_char_wh_ratio_; + // Set to true if fixed pitch should be assumed. + bool fixed_pitch_; + // Cached pointer to dictionary. + const Dict *dict_; + // Debug level for print statements. + int debug_level_; +}; + +} // namespace tesseract + +#endif // TESSERACT_WORDREC_PAIN_POINTS_H_ diff --git a/wordrec/lm_state.cpp b/wordrec/lm_state.cpp new file mode 100644 index 0000000000..827b159f00 --- /dev/null +++ b/wordrec/lm_state.cpp @@ -0,0 +1,81 @@ +/////////////////////////////////////////////////////////////////////// +// File: lm_state.cpp +// Description: Structures and functionality for capturing the state of +// segmentation search guided by the language model. +// Author: Rika Antonova +// Created: Mon Jun 20 11:26:43 PST 2012 +// +// (C) Copyright 2012, Google Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +/////////////////////////////////////////////////////////////////////// + +#include "lm_state.h" + +namespace tesseract { + +ELISTIZE(ViterbiStateEntry); + +void ViterbiStateEntry::Print(const char *msg) const { + tprintf("%s ViterbiStateEntry"); + if (updated) tprintf("(NEW)"); + if (this->debug_str != NULL) { + tprintf(" str=%s", this->debug_str->string()); + } + tprintf(" with ratings_sum=%.4f length=%d cost=%.6f", + this->ratings_sum, this->length, this->cost); + if (this->top_choice_flags) { + tprintf(" top_choice_flags=0x%x", this->top_choice_flags); + } + if (!this->Consistent()) { + tprintf(" inconsistent=(punc %d case %d chartype %d script %d font %d)", + this->consistency_info.NumInconsistentPunc(), + this->consistency_info.NumInconsistentCase(), + this->consistency_info.NumInconsistentChartype(), + this->consistency_info.inconsistent_script, + this->consistency_info.inconsistent_font); + } + if (this->dawg_info) tprintf(" permuter=%d", this->dawg_info->permuter); + if (this->ngram_info) { + tprintf(" ngram_cl_cost=%g context=%s ngram pruned=%d", + this->ngram_info->ngram_and_classifier_cost, + this->ngram_info->context.string(), + this->ngram_info->pruned); + } + if (this->associate_stats.shape_cost > 0.0f) { + tprintf(" shape_cost=%g", this->associate_stats.shape_cost); + } + tprintf(" %s", + XHeightConsistencyEnumName[this->consistency_info.xht_decision]); + + tprintf("\n"); +} + +// Clears the viterbi search state back to its initial conditions. +void LanguageModelState::Clear() { + viterbi_state_entries.clear(); + viterbi_state_entries_prunable_length = 0; + viterbi_state_entries_prunable_max_cost = MAX_FLOAT32; + viterbi_state_entries_length = 0; +} + +void LanguageModelState::Print(const char *msg) { + tprintf("%s VSEs (max_cost=%g prn_len=%d tot_len=%d):\n", + msg, viterbi_state_entries_prunable_max_cost, + viterbi_state_entries_prunable_length, viterbi_state_entries_length); + ViterbiStateEntry_IT vit(&viterbi_state_entries); + for (vit.mark_cycle_pt(); !vit.cycled_list(); vit.forward()) { + vit.data()->Print(""); + } +} + + +} // namespace tesseract diff --git a/wordrec/lm_state.h b/wordrec/lm_state.h new file mode 100644 index 0000000000..0617901916 --- /dev/null +++ b/wordrec/lm_state.h @@ -0,0 +1,239 @@ +/////////////////////////////////////////////////////////////////////// +// File: lm_state.h +// Description: Structures and functionality for capturing the state of +// segmentation search guided by the language model. +// +// Author: Rika Antonova +// Created: Mon Jun 20 11:26:43 PST 2012 +// +// (C) Copyright 2012, Google Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +/////////////////////////////////////////////////////////////////////// + +#ifndef TESSERACT_WORDREC_LANGUAGE_MODEL_DEFS_H_ +#define TESSERACT_WORDREC_LANGUAGE_MODEL_DEFS_H_ + +#include "associate.h" +#include "elst.h" +#include "dawg.h" +#include "lm_consistency.h" +#include "matrix.h" +#include "ratngs.h" +#include "stopper.h" +#include "strngs.h" + +namespace tesseract { + +// Used for expressing various language model flags. +typedef unsigned char LanguageModelFlagsType; + +// The following structs are used for storing the state of the language model +// in the segmentation search graph. In this graph the nodes are BLOB_CHOICEs +// and the links are the relationships between the underlying blobs (see +// segsearch.h for a more detailed description). +// Each of the BLOB_CHOICEs contains LanguageModelState struct, which has +// a list of N best paths (list of ViterbiStateEntry) explored by the Viterbi +// search leading up to and including this BLOB_CHOICE. +// Each ViterbiStateEntry contains information from various components of the +// language model: dawgs in which the path is found, character ngram model +// probability of the path, script/chartype/font consistency info, state for +// language-specific heuristics (e.g. hyphenated and compound words, lower/upper +// case preferences, etc). +// Each ViterbiStateEntry also contains the parent pointer, so that the path +// that it represents (WERD_CHOICE) can be constructed by following these +// parent pointers. + +// Struct for storing additional information used by Dawg language model +// component. It stores the set of active dawgs in which the sequence of +// letters on a path can be found. +struct LanguageModelDawgInfo { + LanguageModelDawgInfo(DawgPositionVector *a, PermuterType pt) : permuter(pt) { + active_dawgs = new DawgPositionVector(*a); + } + ~LanguageModelDawgInfo() { + delete active_dawgs; + } + DawgPositionVector *active_dawgs; + PermuterType permuter; +}; + +// Struct for storing additional information used by Ngram language model +// component. +struct LanguageModelNgramInfo { + LanguageModelNgramInfo(const char *c, int l, bool p, float nc, float ncc) + : context(c), context_unichar_step_len(l), pruned(p), ngram_cost(nc), + ngram_and_classifier_cost(ncc) {} + STRING context; // context string + // Length of the context measured by advancing using UNICHAR::utf8_step() + // (should be at most the order of the character ngram model used). + int context_unichar_step_len; + // The paths with pruned set are pruned out from the perspective of the + // character ngram model. They are explored further because they represent + // a dictionary match or a top choice. Thus ngram_info is still computed + // for them in order to calculate the combined cost. + bool pruned; + // -ln(P_ngram_model(path)) + float ngram_cost; + // -[ ln(P_classifier(path)) + scale_factor * ln(P_ngram_model(path)) ] + float ngram_and_classifier_cost; +}; + +// Struct for storing the information about a path in the segmentation graph +// explored by Viterbi search. +struct ViterbiStateEntry : public ELIST_LINK { + ViterbiStateEntry(ViterbiStateEntry *pe, + BLOB_CHOICE *b, float c, float ol, + const LMConsistencyInfo &ci, + const AssociateStats &as, + LanguageModelFlagsType tcf, + LanguageModelDawgInfo *d, + LanguageModelNgramInfo *n, + const char *debug_uch) + : cost(c), curr_b(b), parent_vse(pe), competing_vse(NULL), + ratings_sum(b->rating()), + min_certainty(b->certainty()), adapted(b->IsAdapted()), length(1), + outline_length(ol), consistency_info(ci), associate_stats(as), + top_choice_flags(tcf), dawg_info(d), ngram_info(n), + updated(true) { + debug_str = (debug_uch == NULL) ? NULL : new STRING(); + if (pe != NULL) { + ratings_sum += pe->ratings_sum; + if (pe->min_certainty < min_certainty) { + min_certainty = pe->min_certainty; + } + adapted += pe->adapted; + length += pe->length; + outline_length += pe->outline_length; + if (debug_uch != NULL) *debug_str += *(pe->debug_str); + } + if (debug_str != NULL && debug_uch != NULL) *debug_str += debug_uch; + } + ~ViterbiStateEntry() { + delete dawg_info; + delete ngram_info; + delete debug_str; + } + // Comparator function for sorting ViterbiStateEntry_LISTs in + // non-increasing order of costs. + static int Compare(const void *e1, const void *e2) { + const ViterbiStateEntry *ve1 = + *reinterpret_cast(e1); + const ViterbiStateEntry *ve2 = + *reinterpret_cast(e2); + return (ve1->cost < ve2->cost) ? -1 : 1; + } + inline bool Consistent() const { + if (dawg_info != NULL && consistency_info.NumInconsistentCase() == 0) { + return true; + } + return consistency_info.Consistent(); + } + // Returns true if this VSE has an alphanumeric character as its classifier + // result. + bool HasAlnumChoice(const UNICHARSET& unicharset) { + if (curr_b == NULL) return false; + UNICHAR_ID unichar_id = curr_b->unichar_id(); + if (unicharset.get_isalpha(unichar_id) || + unicharset.get_isdigit(unichar_id)) + return true; + return false; + } + void Print(const char *msg) const; + + // The cost is an adjusted ratings sum, that is adjusted by all the language + // model components that use Viterbi search. + float cost; + + // Pointers to BLOB_CHOICE and parent ViterbiStateEntry (not owned by this). + BLOB_CHOICE *curr_b; + ViterbiStateEntry *parent_vse; + // Pointer to a case-competing ViterbiStateEntry in the same list that + // represents a path ending in the same letter of the opposite case. + ViterbiStateEntry *competing_vse; + + // Various information about the characters on the path represented + // by this ViterbiStateEntry. + float ratings_sum; // sum of ratings of character on the path + float min_certainty; // minimum certainty on the path + int adapted; // number of BLOB_CHOICES from adapted templates + int length; // number of characters on the path + float outline_length; // length of the outline so far + LMConsistencyInfo consistency_info; // path consistency info + AssociateStats associate_stats; // character widths/gaps/seams + + // Flags for marking the entry as a top choice path with + // the smallest rating or lower/upper case letters). + LanguageModelFlagsType top_choice_flags; + + // Extra information maintained by Dawg laguage model component + // (owned by ViterbiStateEntry). + LanguageModelDawgInfo *dawg_info; + + // Extra information maintained by Ngram laguage model component + // (owned by ViterbiStateEntry). + LanguageModelNgramInfo *ngram_info; + + bool updated; // set to true if the entry has just been created/updated + // UTF8 string representing the path corresponding to this vse. + // Populated only in when language_model_debug_level > 0. + STRING *debug_str; +}; + +ELISTIZEH(ViterbiStateEntry); + +// Struct to store information maintained by various language model components. +struct LanguageModelState { + LanguageModelState() : + viterbi_state_entries_prunable_length(0), + viterbi_state_entries_prunable_max_cost(MAX_FLOAT32), + viterbi_state_entries_length(0) {} + ~LanguageModelState() {} + + // Clears the viterbi search state back to its initial conditions. + void Clear(); + + void Print(const char *msg); + + // Storage for the Viterbi state. + ViterbiStateEntry_LIST viterbi_state_entries; + // Number and max cost of prunable paths in viterbi_state_entries. + int viterbi_state_entries_prunable_length; + float viterbi_state_entries_prunable_max_cost; + // Total number of entries in viterbi_state_entries. + int viterbi_state_entries_length; +}; + +// Bundle together all the things pertaining to the best choice/state. +struct BestChoiceBundle { + explicit BestChoiceBundle(int matrix_dimension) + : updated(false), best_vse(NULL) { + beam.reserve(matrix_dimension); + for (int i = 0; i < matrix_dimension; ++i) + beam.push_back(new LanguageModelState); + } + ~BestChoiceBundle() {} + + // Flag to indicate whether anything was changed. + bool updated; + // Places to try to fix the word suggested by ambiguity checking. + DANGERR fixpt; + // The beam. One LanguageModelState containing a list of ViterbiStateEntry per + // row in the ratings matrix containing all VSEs whose BLOB_CHOICE is + // somewhere in the corresponding row. + PointerVector beam; + // Best ViterbiStateEntry and BLOB_CHOICE. + ViterbiStateEntry *best_vse; +}; + +} // namespace tesseract + +#endif // TESSERACT_WORDREC_LANGUAGE_MODEL_DEFS_H_ diff --git a/wordrec/matchtab.cpp b/wordrec/matchtab.cpp deleted file mode 100644 index b3e3f10d1a..0000000000 --- a/wordrec/matchtab.cpp +++ /dev/null @@ -1,214 +0,0 @@ -/* -*-C-*- - ******************************************************************************** - * - * File: matchtab.c (Formerly matchtab.c) - * Description: Match table to retain blobs that were matched. - * Author: Mark Seaman, OCR Technology - * Created: Mon Jan 29 09:00:56 1990 - * Modified: Tue Mar 19 15:09:06 1991 (Mark Seaman) marks@hpgrlt - * Language: C - * Package: N/A - * Status: Experimental (Do Not Distribute) - * - * (c) Copyright 1990, Hewlett-Packard Company. - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** http://www.apache.org/licenses/LICENSE-2.0 - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - * - *********************************************************************************/ -#include "matchtab.h" - -#include "blobs.h" -#include "callcpp.h" -#include "elst.h" -#include "freelist.h" -#include "helpers.h" -#include "ratngs.h" - -#define NUM_MATCH_ENTRIES 500 /* Entries in match_table */ - -namespace tesseract { - -BlobMatchTable::BlobMatchTable() - : been_initialized_(false), match_table_(NULL) { - init_match_table(); -} - -BlobMatchTable::~BlobMatchTable() { - end_match_table(); -} - -/********************************************************************** - * init_match_table - * - * Create and clear a match table to be used to speed up the splitter. - **********************************************************************/ -void BlobMatchTable::init_match_table() { - if (been_initialized_) { - /* Reclaim old choices */ - for (int x = 0; x < NUM_MATCH_ENTRIES; x++) { - if (!IsEmpty(x)) { - match_table_[x].rating->clear(); - delete match_table_[x].rating; - // Reinitialize the entry. - match_table_[x].box = TBOX(); - match_table_[x].rating = NULL; - } - } - } else { - /* Allocate memory once */ - match_table_ = new MATCH[NUM_MATCH_ENTRIES]; - been_initialized_ = true; - } -} - -void BlobMatchTable::end_match_table() { - if (been_initialized_) { - init_match_table(); - delete[] match_table_; - match_table_ = NULL; - been_initialized_ = false; - } -} - - -/********************************************************************** - * put_match - * - * Put a new blob and its corresponding match ratings into the match - * table. - **********************************************************************/ -void BlobMatchTable::put_match(TBLOB *blob, BLOB_CHOICE_LIST *ratings) { - if (!blob) return; - /* Hash into table */ - TBOX bbox(blob->bounding_box()); - int start = Hash(bbox); - - /* Look for empty */ - int x = start; - do { - if (IsEmpty(x)) { - /* Add this entry */ - match_table_[x].box = bbox; - // Copy ratings to match_table_[x].rating - match_table_[x].rating = new BLOB_CHOICE_LIST(); - match_table_[x].rating->deep_copy(ratings, &BLOB_CHOICE::deep_copy); - return; - } - if (++x >= NUM_MATCH_ENTRIES) - x = 0; - } while (x != start); - - cprintf ("error: Match table is full\n"); -} - - -/********************************************************************** - * get_match - * - * Look up this blob in the match table to see if it needs to be - * matched. If it is not present then NULL is returned. - **********************************************************************/ -BLOB_CHOICE_LIST *BlobMatchTable::get_match(TBLOB *blob) { - return get_match_by_box(blob->bounding_box()); -} - -/********************************************************************** - * Hash - * - * The hash function we use to translate a bounding box to a starting - * hash position in our array. - **********************************************************************/ -int BlobMatchTable::Hash(const TBOX &box) const { - int topleft = (box.top() << 16) + box.left(); - int botright = (box.bottom() << 16) + box.right(); - return Modulo(topleft + botright, NUM_MATCH_ENTRIES); -} - -/********************************************************************** - * IsEmpty - * - * Returns whether the idx entry in the array is still empty. - **********************************************************************/ -bool BlobMatchTable::IsEmpty(int idx) const { - return TBOX() == match_table_[idx].box && - NULL == match_table_[idx].rating; -} - -/********************************************************************** - * get_match_by_box - * - * Look up this blob in the match table to see if it needs to be - * matched. If it is not present then NULL is returned. - **********************************************************************/ -BLOB_CHOICE_LIST *BlobMatchTable::get_match_by_box(const TBOX &box) { - int start = Hash(box); - int x = start; - /* Search for match */ - do { - /* Not found when blank */ - if (IsEmpty(x)) - break; - /* Is this the match ? */ - if (match_table_[x].box == box) { - BLOB_CHOICE_LIST *blist = new BLOB_CHOICE_LIST(); - blist->deep_copy(match_table_[x].rating, &BLOB_CHOICE::deep_copy); - return blist; - } - if (++x >= NUM_MATCH_ENTRIES) - x = 0; - } while (x != start); - return NULL; -} - -/********************************************************************** - * add_to_match - * - * Update ratings list in the match_table corresponding to the given - * blob. The function assumes that: - * -- the match table contains the initial non-NULL list with choices - * for the given blob - * -- the new ratings list is a superset of the corresponding list in - * the match_table and the unichar ids of the blob choices in the - * list are unique. - * The entries that appear in the new ratings list and not in the - * old one are added to the old ratings list in the match_table. - **********************************************************************/ -void BlobMatchTable::add_to_match(TBLOB *blob, BLOB_CHOICE_LIST *ratings) { - TBOX bbox = blob->bounding_box(); - int start = Hash(bbox); - int x = start; - do { - if (IsEmpty(x)) { - fprintf(stderr, "Can not update uninitialized entry in match_table\n"); - ASSERT_HOST(!IsEmpty(x)); - } - if (match_table_[x].box == bbox) { - // Copy new ratings to match_table_[x].rating. - BLOB_CHOICE_IT it; - it.set_to_list(match_table_[x].rating); - BLOB_CHOICE_IT new_it; - new_it.set_to_list(ratings); - assert(it.length() <= new_it.length()); - for (it.mark_cycle_pt(), new_it.mark_cycle_pt(); - !it.cycled_list() && !new_it.cycled_list(); new_it.forward()) { - if (it.data()->unichar_id() == new_it.data()->unichar_id()) { - it.forward(); - } else { - it.add_before_stay_put(new BLOB_CHOICE(*(new_it.data()))); - } - } - return; - } - if (++x >= NUM_MATCH_ENTRIES) - x = 0; - } while (x != start); -} - -} // namespace tesseract diff --git a/wordrec/matchtab.h b/wordrec/matchtab.h deleted file mode 100644 index 5e2d164857..0000000000 --- a/wordrec/matchtab.h +++ /dev/null @@ -1,64 +0,0 @@ -/* -*-C-*- - ******************************************************************************** - * - * File: matchtab.h (Formerly matchtab.h) - * Description: Match table to retain blobs that were matched. - * Author: Mark Seaman, OCR Technology - * Created: Mon Jan 29 09:00:56 1990 - * Modified: Tue Mar 19 15:38:19 1991 (Mark Seaman) marks@hpgrlt - * Language: C - * Package: N/A - * Status: Experimental (Do Not Distribute) - * - * (c) Copyright 1990, Hewlett-Packard Company. - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** http://www.apache.org/licenses/LICENSE-2.0 - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - * - *********************************************************************************/ -#ifndef MATCHTAB_H -#define MATCHTAB_H - -#include "ratngs.h" -#include "blobs.h" - -namespace tesseract { - -struct MATCH { - MATCH() : rating(NULL) {} - TBOX box; - BLOB_CHOICE_LIST *rating; -}; - -// A class for mapping rectangular bounding boxes to choice lists. -// Only meant to be used at the word level, as we have a limit of -// 500 recognition lists for all subsequences of blobs. -class BlobMatchTable { - public: - BlobMatchTable(); - ~BlobMatchTable(); - - void init_match_table(); - void end_match_table(); - void put_match(TBLOB *blob, BLOB_CHOICE_LIST *ratings); - BLOB_CHOICE_LIST *get_match(TBLOB *blob); - BLOB_CHOICE_LIST *get_match_by_box(const TBOX &box); - void add_to_match(TBLOB *blob, BLOB_CHOICE_LIST *ratings); - - private: - int Hash(const TBOX &box) const; - // Returns whether the idx entry in the array is still empty. - bool IsEmpty(int idx) const; - - bool been_initialized_; - MATCH* match_table_; -}; - -} -#endif diff --git a/wordrec/params_model.cpp b/wordrec/params_model.cpp new file mode 100644 index 0000000000..a77d2b13b6 --- /dev/null +++ b/wordrec/params_model.cpp @@ -0,0 +1,174 @@ +/////////////////////////////////////////////////////////////////////// +// File: params_model.cpp +// Description: Trained language model parameters. +// Author: David Eger +// Created: Mon Jun 11 11:26:42 PDT 2012 +// +// (C) Copyright 2012, Google Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +/////////////////////////////////////////////////////////////////////// + +#include "params_model.h" + +#include +#include +#include + +#include "bitvector.h" +#include "tprintf.h" + +namespace tesseract { + +// Scale factor to apply to params model scores. +static const float kScoreScaleFactor = 100.0f; +// Minimum cost result to return. +static const float kMinFinalCost = 0.001f; +// Maximum cost result to return. +static const float kMaxFinalCost = 100.0f; + +void ParamsModel::Print() { + for (int p = 0; p < PTRAIN_NUM_PASSES; ++p) { + tprintf("ParamsModel for pass %d lang %s\n", p, lang_.string()); + for (int i = 0; i < weights_vec_[p].size(); ++i) { + tprintf("%s = %g\n", kParamsTrainingFeatureTypeName[i], + weights_vec_[p][i]); + } + } +} + +void ParamsModel::Copy(const ParamsModel &other_model) { + for (int p = 0; p < PTRAIN_NUM_PASSES; ++p) { + weights_vec_[p] = other_model.weights_for_pass( + static_cast(p)); + } +} + +// Given a (modifiable) line, parse out a key / value pair. +// Return true on success. +bool ParamsModel::ParseLine(char *line, char** key, float *val) { + if (line[0] == '#') + return false; + int end_of_key = 0; + while (line[end_of_key] && !isspace(line[end_of_key])) end_of_key++; + if (!line[end_of_key]) { + tprintf("ParamsModel::Incomplete line %s\n", line); + return false; + } + line[end_of_key++] = 0; + *key = line; + if (sscanf(line + end_of_key, " %f", val) != 1) + return false; + return true; +} + +// Applies params model weights to the given features. +// Assumes that features is an array of size PTRAIN_NUM_FEATURE_TYPES. +// The cost is set to a number that can be multiplied by the outline length, +// as with the old ratings scheme. This enables words of different length +// and combinations of words to be compared meaningfully. +float ParamsModel::ComputeCost(const float features[]) const { + float unnorm_score = 0.0; + for (int f = 0; f < PTRAIN_NUM_FEATURE_TYPES; ++f) { + unnorm_score += weights_vec_[pass_][f] * features[f]; + } + return ClipToRange(-unnorm_score / kScoreScaleFactor, + kMinFinalCost, kMaxFinalCost); +} + +bool ParamsModel::Equivalent(const ParamsModel &that) const { + float epsilon = 0.0001; + for (int p = 0; p < PTRAIN_NUM_PASSES; ++p) { + if (weights_vec_[p].size() != that.weights_vec_[p].size()) return false; + for (int i = 0; i < weights_vec_[p].size(); i++) { + if (weights_vec_[p][i] != that.weights_vec_[p][i] && + fabs(weights_vec_[p][i] - that.weights_vec_[p][i]) > epsilon) + return false; + } + } + return true; +} + +bool ParamsModel::LoadFromFile( + const char *lang, + const char *full_path) { + FILE *fp = fopen(full_path, "rb"); + if (!fp) { + tprintf("Error opening file %s\n", full_path); + return false; + } + bool result = LoadFromFp(lang, fp, -1); + fclose(fp); + return result; +} + +bool ParamsModel::LoadFromFp(const char *lang, FILE *fp, inT64 end_offset) { + const int kMaxLineSize = 100; + char line[kMaxLineSize]; + BitVector present; + present.Init(PTRAIN_NUM_FEATURE_TYPES); + lang_ = lang; + // Load weights for passes with adaption on. + GenericVector &weights = weights_vec_[pass_]; + weights.init_to_size(PTRAIN_NUM_FEATURE_TYPES, 0.0); + + while ((end_offset < 0 || ftell(fp) < end_offset) && + fgets(line, kMaxLineSize, fp)) { + char *key = NULL; + float value; + if (!ParseLine(line, &key, &value)) + continue; + int idx = ParamsTrainingFeatureByName(key); + if (idx < 0) { + tprintf("ParamsModel::Unknown parameter %s\n", key); + continue; + } + if (!present[idx]) { + present.SetValue(idx, true); + } + weights[idx] = value; + } + bool complete = (present.NumSetBits() == PTRAIN_NUM_FEATURE_TYPES); + if (!complete) { + for (int i = 0; i < PTRAIN_NUM_FEATURE_TYPES; i++) { + if (!present[i]) { + tprintf("Missing field %s.\n", kParamsTrainingFeatureTypeName[i]); + } + } + lang_ = ""; + weights.truncate(0); + } + return complete; +} + +bool ParamsModel::SaveToFile(const char *full_path) const { + const GenericVector &weights = weights_vec_[pass_]; + if (weights.size() != PTRAIN_NUM_FEATURE_TYPES) { + tprintf("Refusing to save ParamsModel that has not been initialized.\n"); + return false; + } + FILE *fp = fopen(full_path, "wb"); + if (!fp) { + tprintf("Could not open %s for writing.\n", full_path); + return false; + } + bool all_good = true; + for (int i = 0; i < weights.size(); i++) { + if (fprintf(fp, "%s %f\n", kParamsTrainingFeatureTypeName[i], weights[i]) + < 0) { + all_good = false; + } + } + fclose(fp); + return all_good; +} + +} // namespace tesseract diff --git a/wordrec/params_model.h b/wordrec/params_model.h new file mode 100644 index 0000000000..a66e4450de --- /dev/null +++ b/wordrec/params_model.h @@ -0,0 +1,89 @@ +/////////////////////////////////////////////////////////////////////// +// File: params_model.h +// Description: Trained feature serialization for language parameter training. +// Author: David Eger +// Created: Mon Jun 11 11:26:42 PDT 2012 +// +// (C) Copyright 2011, Google Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +/////////////////////////////////////////////////////////////////////// + +#ifndef TESSERACT_WORDREC_PARAMS_MODEL_H_ +#define TESSERACT_WORDREC_PARAMS_MODEL_H_ + +#include "params_training_featdef.h" +#include "ratngs.h" +#include "strngs.h" + +namespace tesseract { + +// Represents the learned weights for a given language. +class ParamsModel { + public: + // Enum for expressing OCR pass. + enum PassEnum { + PTRAIN_PASS1, + PTRAIN_PASS2, + + PTRAIN_NUM_PASSES + }; + + ParamsModel() : pass_(PTRAIN_PASS1) {} + ParamsModel(const char *lang, const GenericVector &weights) : + lang_(lang), pass_(PTRAIN_PASS1) { weights_vec_[pass_] = weights; } + inline bool Initialized() { + return weights_vec_[pass_].size() == PTRAIN_NUM_FEATURE_TYPES; + } + // Prints out feature weights. + void Print(); + // Clears weights for all passes. + void Clear() { + for (int p = 0; p < PTRAIN_NUM_PASSES; ++p) weights_vec_[p].clear(); + } + // Copies the weights of the given params model. + void Copy(const ParamsModel &other_model); + // Applies params model weights to the given features. + // Assumes that features is an array of size PTRAIN_NUM_FEATURE_TYPES. + float ComputeCost(const float features[]) const; + bool Equivalent(const ParamsModel &that) const; + + // Returns true on success. + bool SaveToFile(const char *full_path) const; + + // Returns true on success. + bool LoadFromFile(const char *lang, const char *full_path); + bool LoadFromFp(const char *lang, FILE *fp, inT64 end_offset); + + const GenericVector& weights() const { + return weights_vec_[pass_]; + } + const GenericVector& weights_for_pass(PassEnum pass) const { + return weights_vec_[pass]; + } + void SetPass(PassEnum pass) { pass_ = pass; } + + private: + bool ParseLine(char *line, char **key, float *val); + + STRING lang_; + // Set to the current pass type and used to determine which set of weights + // should be used for ComputeCost() and other functions. + PassEnum pass_; + // Several sets of weights for various OCR passes (e.g. pass1 with adaption, + // pass2 without adaption, etc). + GenericVector weights_vec_[PTRAIN_NUM_PASSES]; +}; + +} // namespace tesseract + +#endif // TESSERACT_WORDREC_PARAMS_MODEL_H_ + diff --git a/wordrec/pieces.cpp b/wordrec/pieces.cpp index 12dd098f46..529793a534 100644 --- a/wordrec/pieces.cpp +++ b/wordrec/pieces.cpp @@ -29,14 +29,10 @@ #include "blobs.h" #include "freelist.h" #include "helpers.h" -#include "matchtab.h" #include "matrix.h" #include "ndminx.h" -#include "plotseg.h" #include "ratngs.h" #include "seam.h" -#include "states.h" -#include "wordclass.h" #include "wordrec.h" // Include automatically generated configuration file if running autoconf. @@ -48,22 +44,6 @@ F u n c t i o n s ----------------------------------------------------------------------*/ - -/********************************************************************** - * bounds_of_piece - * - * Find the bounds of the piece that will be created by joining the - * requested collection of pieces together. - **********************************************************************/ -TBOX bounds_of_piece(TBOX *bounds, inT16 start, inT16 end) { - TBOX all_together = bounds[start]; - for (int x = start + 1; x <= end; x++) { - all_together += bounds[x]; - } - return all_together; -} - - /********************************************************************** * classify_piece * @@ -72,34 +52,22 @@ TBOX bounds_of_piece(TBOX *bounds, inT16 start, inT16 end) { * the collection of small pieces un modified. **********************************************************************/ namespace tesseract { -BLOB_CHOICE_LIST *Wordrec::classify_piece(TBLOB *pieces, - const DENORM& denorm, - SEAMS seams, +BLOB_CHOICE_LIST *Wordrec::classify_piece(const GenericVector& seams, inT16 start, inT16 end, + const char* description, + TWERD *word, BlamerBundle *blamer_bundle) { - BLOB_CHOICE_LIST *choices; - TBLOB *blob; - inT16 x; - - join_pieces(pieces, seams, start, end); - for (blob = pieces, x = 0; x < start; x++) { - blob = blob->next; - } - choices = classify_blob(blob, denorm, "pieces:", White, blamer_bundle); - - break_pieces(blob, seams, start, end); -#ifndef GRAPHICS_DISABLED - if (wordrec_display_segmentations > 2) { - STATE current_state; - SEARCH_STATE chunk_groups; - set_n_ones (¤t_state, array_count(seams)); - chunk_groups = bin_to_chunks(¤t_state, array_count(seams)); - display_segmentation(pieces, chunk_groups); - window_wait(segm_window); - memfree(chunk_groups); + if (end > start) join_pieces(seams, start, end, word); + BLOB_CHOICE_LIST *choices = classify_blob(word->blobs[start], description, + White, blamer_bundle); + // Set the matrix_cell_ entries in all the BLOB_CHOICES. + BLOB_CHOICE_IT bc_it(choices); + for (bc_it.mark_cycle_pt(); !bc_it.cycled_list(); bc_it.forward()) { + bc_it.data()->set_matrix_cell(start, end); } -#endif + + if (end > start) break_pieces(seams, start, end, word); return (choices); } @@ -230,10 +198,11 @@ void Wordrec::merge_and_put_fragment_lists(inT16 row, inT16 column, UNICHAR_ID merged_unichar_id = first_unichar_id; inT16 merged_fontinfo_id = choice_lists_it[0].data()->fontinfo_id(); inT16 merged_fontinfo_id2 = choice_lists_it[0].data()->fontinfo_id2(); - inT16 merged_min_xheight = choice_lists_it[0].data()->min_xheight(); - inT16 merged_max_xheight = choice_lists_it[0].data()->max_xheight(); + float merged_min_xheight = choice_lists_it[0].data()->min_xheight(); + float merged_max_xheight = choice_lists_it[0].data()->max_xheight(); + float positive_yshift = 0, negative_yshift = 0; int merged_script_id = choice_lists_it[0].data()->script_id(); - bool merged_adapted = choice_lists_it[0].data()->adapted(); + BlobChoiceClassifier classifier = choice_lists_it[0].data()->classifier(); float merged_rating = 0, merged_certainty = 0; for (int i = 0; i < num_frag_parts; i++) { @@ -250,8 +219,14 @@ void Wordrec::merge_and_put_fragment_lists(inT16 row, inT16 column, IntersectRange(choice_lists_it[i].data()->min_xheight(), choice_lists_it[i].data()->max_xheight(), &merged_min_xheight, &merged_max_xheight); + float yshift = choice_lists_it[i].data()->yshift(); + if (yshift > positive_yshift) positive_yshift = yshift; + if (yshift < negative_yshift) negative_yshift = yshift; } + float merged_yshift = positive_yshift != 0 + ? (negative_yshift != 0 ? 0 : positive_yshift) + : negative_yshift; merged_choice_it.add_to_end(new BLOB_CHOICE(merged_unichar_id, merged_rating, merged_certainty, @@ -260,7 +235,8 @@ void Wordrec::merge_and_put_fragment_lists(inT16 row, inT16 column, merged_script_id, merged_min_xheight, merged_max_xheight, - merged_adapted)); + merged_yshift, + classifier)); } } @@ -351,86 +327,4 @@ void Wordrec::merge_fragments(MATRIX *ratings, inT16 num_blobs) { } -/********************************************************************** - * get_piece_rating - * - * Check to see if this piece has already been classified. If it has - * return that rating. Otherwise build the piece from the smaller - * pieces, classify it, store the rating for later, and take the piece - * apart again. - **********************************************************************/ -BLOB_CHOICE_LIST *Wordrec::get_piece_rating(MATRIX *ratings, - TBLOB *blobs, - const DENORM& denorm, - SEAMS seams, - inT16 start, - inT16 end, - BlamerBundle *blamer_bundle) { - BLOB_CHOICE_LIST *choices = ratings->get(start, end); - if (choices == NOT_CLASSIFIED) { - choices = classify_piece(blobs, - denorm, - seams, - start, - end, - blamer_bundle); - ratings->put(start, end, choices); - if (wordrec_debug_level > 1) { - tprintf("get_piece_rating(): updated ratings matrix\n"); - ratings->print(getDict().getUnicharset()); - } - } - return (choices); -} - - -/********************************************************************** - * record_blob_bounds - * - * Set up and initialize an array that holds the bounds of a set of - * blobs. Caller should delete[] the array. - **********************************************************************/ -TBOX *Wordrec::record_blob_bounds(TBLOB *blobs) { - int nblobs = count_blobs(blobs); - TBOX *bboxes = new TBOX[nblobs]; - - inT16 x = 0; - for (TBLOB* blob = blobs; blob != NULL; blob = blob->next) { - bboxes[x] = blob->bounding_box(); - x++; - } - return bboxes; -} - - -/********************************************************************** - * record_piece_ratings - * - * Save the choices for all the pieces that have been classified into - * a matrix that can be used to look them up later. A two dimensional - * matrix is created. The indices correspond to the starting and - * ending initial piece number. - **********************************************************************/ -MATRIX *Wordrec::record_piece_ratings(TBLOB *blobs) { - inT16 num_blobs = count_blobs(blobs); - TBOX *bounds = record_blob_bounds(blobs); - MATRIX *ratings = new MATRIX(num_blobs); - - for (int x = 0; x < num_blobs; x++) { - for (int y = x; y < num_blobs; y++) { - TBOX piecebox = bounds_of_piece(bounds, x, y); - BLOB_CHOICE_LIST *choices = blob_match_table.get_match_by_box(piecebox); - if (choices != NULL) { - ratings->put(x, y, choices); - } - } - } - - if (merge_fragments_in_matrix) - merge_fragments(ratings, num_blobs); - - delete []bounds; - return ratings; -} - } // namespace tesseract diff --git a/wordrec/plotseg.cpp b/wordrec/plotseg.cpp deleted file mode 100644 index fe9999972c..0000000000 --- a/wordrec/plotseg.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/* -*-C-*- - ******************************************************************************** - * - * File: plotseg.c (Formerly plotseg.c) - * Description: - * Author: Mark Seaman, OCR Technology - * Created: Fri Oct 16 14:37:00 1987 - * Modified: Fri Apr 26 10:03:05 1991 (Mark Seaman) marks@hpgrlt - * Language: C - * Package: N/A - * Status: Reusable Software Component - * - * (c) Copyright 1987, Hewlett-Packard Company. - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** http://www.apache.org/licenses/LICENSE-2.0 - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - * - *********************************************************************************/ - -// Include automatically generated configuration file if running autoconf. -#ifdef HAVE_CONFIG_H -#include "config_auto.h" -#endif - -#ifndef GRAPHICS_DISABLED - -/*---------------------------------------------------------------------- - I n c l u d e s -----------------------------------------------------------------------*/ -#include "plotseg.h" -#include "callcpp.h" -#include "scrollview.h" -#include "blobs.h" -#include "const.h" -#include - -/*---------------------------------------------------------------------- - V a r i a b l e s -----------------------------------------------------------------------*/ -ScrollView *segm_window = NULL; - -INT_VAR(wordrec_display_segmentations, 0, "Display Segmentations"); - -/*---------------------------------------------------------------------- - F u n c t i o n s -----------------------------------------------------------------------*/ -/********************************************************************** - * display_segmentation - * - * Display all the words on the page into a window. - **********************************************************************/ -void display_segmentation(TBLOB *chunks, SEARCH_STATE segmentation) { - /* If no window create it */ - if (segm_window == NULL) { - segm_window = c_create_window ("Segmentation", 5, 10, - 500, 256, -1000.0, 1000.0, 0.0, 256.0); - } - else { - c_clear_window(segm_window); - } - - render_segmentation(segm_window, chunks, segmentation); - /* Put data in the window */ - c_make_current(segm_window); -} - -/********************************************************************** - * render_segmentation - * - * Create a list of line segments that represent the list of chunks - * using the correct segmentation that was supplied as input. - **********************************************************************/ -void render_segmentation(ScrollView *window, - TBLOB *chunks, - SEARCH_STATE segmentation) { - TBLOB *blob; - C_COL color = Black; - int char_num = -1; - int chunks_left = 0; - - TBOX bbox; - if (chunks) bbox = chunks->bounding_box(); - - for (blob = chunks; blob != NULL; blob = blob->next) { - bbox += blob->bounding_box(); - if (chunks_left-- == 0) { - color = color_list[++char_num % NUM_COLORS]; - - if (char_num < segmentation[0]) - chunks_left = segmentation[char_num + 1]; - else - chunks_left = MAX_INT32; - } - render_outline(window, blob->outlines, color); - } - window->ZoomToRectangle(bbox.left(), bbox.top(), - bbox.right(), bbox.bottom()); -} - -#endif // GRPAHICS_DISABLED diff --git a/wordrec/plotseg.h b/wordrec/plotseg.h deleted file mode 100644 index 3d1367f007..0000000000 --- a/wordrec/plotseg.h +++ /dev/null @@ -1,48 +0,0 @@ -/* -*-C-*- - ******************************************************************************** - * - * File: plotseg.h (Formerly plotseg.h) - * Description: - * Author: Mark Seaman, SW Productivity - * Created: Fri Oct 16 14:37:00 1987 - * Modified: Fri Apr 26 10:03:32 1991 (Mark Seaman) marks@hpgrlt - * Language: C - * Package: N/A - * Status: Reusable Software Component - * - * (c) Copyright 1987, Hewlett-Packard Company. - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** http://www.apache.org/licenses/LICENSE-2.0 - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - * - *********************************************************************************/ -#ifndef PLOTSEG_H -#define PLOTSEG_H - -/*---------------------------------------------------------------------- - I n c l u d e s -----------------------------------------------------------------------*/ -#include "states.h" -#include "render.h" - -/*---------------------------------------------------------------------- - V a r i a b l e s -----------------------------------------------------------------------*/ -extern ScrollView *segm_window; -extern INT_VAR_H(wordrec_display_segmentations, 0, "Display Segmentations"); - -/*---------------------------------------------------------------------- - F u n c t i o n s -----------------------------------------------------------------------*/ -void display_segmentation(TBLOB *chunks, SEARCH_STATE segmentation); - -void render_segmentation(ScrollView *window, - TBLOB *chunks, - SEARCH_STATE segmentation); -#endif diff --git a/wordrec/segsearch.cpp b/wordrec/segsearch.cpp index db2a232f20..40a52f371a 100644 --- a/wordrec/segsearch.cpp +++ b/wordrec/segsearch.cpp @@ -20,59 +20,34 @@ #include "wordrec.h" #include "associate.h" -#include "baseline.h" #include "language_model.h" #include "matrix.h" -#include "oldheap.h" #include "params.h" +#include "lm_pain_points.h" #include "ratngs.h" -#include "states.h" - -ELISTIZE(SEG_SEARCH_PENDING); namespace tesseract { -void Wordrec::SegSearch(CHUNKS_RECORD *chunks_record, - WERD_CHOICE *best_choice, - BLOB_CHOICE_LIST_VECTOR *best_char_choices, - WERD_CHOICE *raw_choice, - STATE *output_best_state, - BlamerBundle *blamer_bundle) { - int row, col = 0; +void Wordrec::DoSegSearch(WERD_RES* word_res) { + BestChoiceBundle best_choice_bundle(word_res->ratings->dimension()); + // Run Segmentation Search. + SegSearch(word_res, &best_choice_bundle, NULL); +} + +void Wordrec::SegSearch(WERD_RES* word_res, + BestChoiceBundle* best_choice_bundle, + BlamerBundle* blamer_bundle) { if (segsearch_debug_level > 0) { - tprintf("Starting SegSearch on ratings matrix:\n"); - chunks_record->ratings->print(getDict().getUnicharset()); + tprintf("Starting SegSearch on ratings matrix%s:\n", + wordrec_enable_assoc ? " (with assoc)" : ""); + word_res->ratings->print(getDict().getUnicharset()); } - // Start with a fresh best_choice since rating adjustments - // used by the chopper and the new segmentation search are not compatible. - best_choice->set_rating(WERD_CHOICE::kBadRating); - // TODO(antonova): Due to the fact that we currently do not re-start the - // segmentation search from the best choice the chopper found, sometimes - // the the segmentation search does not find the best path (that chopper - // did discover) and does not have a chance to adapt to it. As soon as we - // transition to using new-style language model penalties in the chopper - // this issue will be resolved. But for how we are forced clear the - // accumulator choices. - // - // Clear best choice accumulator (that is used for adaption), so that - // choices adjusted by chopper do not interfere with the results from the - // segmentation search. - getDict().ClearBestChoiceAccum(); + LMPainPoints pain_points(segsearch_max_pain_points, + segsearch_max_char_wh_ratio, + assume_fixed_pitch_char_segment, + &getDict(), segsearch_debug_level); - MATRIX *ratings = chunks_record->ratings; - // Priority queue containing pain points generated by the language model - // The priority is set by the language model components, adjustments like - // seam cost and width priority are factored into the priority. - HEAP *pain_points = MakeHeap(segsearch_max_pain_points); - - // best_path_by_column records the lowest cost path found so far for each - // column of the chunks_record->ratings matrix over all the rows. - BestPathByColumn *best_path_by_column = - new BestPathByColumn[ratings->dimension()]; - for (col = 0; col < ratings->dimension(); ++col) { - best_path_by_column[col].avg_cost = WERD_CHOICE::kBadRating; - best_path_by_column[col].best_vse = NULL; - } + pain_points.GenerateInitial(word_res); // Compute scaling factor that will help us recover blob outline length // from classifier rating and certainty for the blob. @@ -80,197 +55,213 @@ void Wordrec::SegSearch(CHUNKS_RECORD *chunks_record, language_model_->InitForWord(prev_word_best_choice_, assume_fixed_pitch_char_segment, - best_choice->certainty(), - segsearch_max_char_wh_ratio, rating_cert_scale, - pain_points, chunks_record, blamer_bundle, - wordrec_debug_blamer); + segsearch_max_char_wh_ratio, rating_cert_scale); - MATRIX_COORD *pain_point; + // Initialize blamer-related information: map character boxes recorded in + // blamer_bundle->norm_truth_word to the corresponding i,j indices in the + // ratings matrix. We expect this step to succeed, since when running the + // chopper we checked that the correct chops are present. + if (blamer_bundle != NULL) { + blamer_bundle->SetupCorrectSegmentation(word_res->chopped_word, + wordrec_debug_blamer); + } + + MATRIX_COORD pain_point; float pain_point_priority; - BestChoiceBundle best_choice_bundle( - output_best_state, best_choice, raw_choice, best_char_choices); - // pending[i] stores a list of the parent/child pair of BLOB_CHOICE_LISTs, - // where i is the column of the child. Initially all the classified entries - // in the ratings matrix from column 0 (with parent NULL) are inserted into - // pending[0]. As the language model state is updated, new child/parent - // pairs are inserted into the lists. Next, the entries in pending[1] are - // considered, and so on. It is important that during the update the + // pending[col] tells whether there is update work to do to combine + // best_choice_bundle->beam[col - 1] with some BLOB_CHOICEs in matrix[col, *]. + // As the language model state is updated, pending entries are modified to + // minimize duplication of work. It is important that during the update the // children are considered in the non-decreasing order of their column, since // this guarantees that all the parents would be up to date before an update // of a child is done. - SEG_SEARCH_PENDING_LIST *pending = - new SEG_SEARCH_PENDING_LIST[ratings->dimension()]; + GenericVector pending; + pending.init_to_size(word_res->ratings->dimension(), SegSearchPending()); + + // Search the ratings matrix for the initial best path. + pending[0].SetColumnClassified(); + UpdateSegSearchNodes(rating_cert_scale, 0, &pending, word_res, + &pain_points, best_choice_bundle, blamer_bundle); - // Search for the ratings matrix for the initial best path. - for (row = 0; row < ratings->dimension(); ++row) { - if (ratings->get(0, row) != NOT_CLASSIFIED) { - pending[0].add_sorted( - SEG_SEARCH_PENDING::compare, true, - new SEG_SEARCH_PENDING(row, NULL, LanguageModel::kAllChangedFlag)); + if (!SegSearchDone(0)) { // find a better choice + if (chop_enable && word_res->chopped_word != NULL) { + improve_by_chopping(rating_cert_scale, word_res, best_choice_bundle, + blamer_bundle, &pain_points, &pending); } - } - UpdateSegSearchNodes(0, &pending, &best_path_by_column, chunks_record, - pain_points, &best_choice_bundle, blamer_bundle); + if (chop_debug) + print_seams("Final seam list:", word_res->seam_array); + if (blamer_bundle != NULL && + !blamer_bundle->ChoiceIsCorrect(word_res->best_choice)) { + blamer_bundle->SetChopperBlame(word_res, wordrec_debug_blamer); + } + } // Keep trying to find a better path by fixing the "pain points". int num_futile_classifications = 0; STRING blamer_debug; - while (!SegSearchDone(num_futile_classifications) || - (blamer_bundle != NULL && - blamer_bundle->segsearch_is_looking_for_blame)) { + while (wordrec_enable_assoc && + (!SegSearchDone(num_futile_classifications) || + (blamer_bundle != NULL && + blamer_bundle->GuidedSegsearchStillGoing()))) { // Get the next valid "pain point". - int pop; - while (true) { - pop = HeapPop(pain_points, &pain_point_priority, &pain_point); - if (pop == EMPTY) break; - if (pain_point->Valid(*ratings) && - ratings->get(pain_point->col, pain_point->row) == NOT_CLASSIFIED) { + bool found_nothing = true; + LMPainPointsType pp_type; + while ((pp_type = pain_points.Deque(&pain_point, &pain_point_priority)) != + LM_PPTYPE_NUM) { + if (!pain_point.Valid(*word_res->ratings)) { + word_res->ratings->IncreaseBandSize( + pain_point.row - pain_point.col + 1); + } + if (pain_point.Valid(*word_res->ratings) && + !word_res->ratings->Classified(pain_point.col, pain_point.row, + getDict().WildcardID())) { + found_nothing = false; break; - } else { - delete pain_point; } } - if (pop == EMPTY) { + if (found_nothing) { if (segsearch_debug_level > 0) tprintf("Pain points queue is empty\n"); break; } - ProcessSegSearchPainPoint(pain_point_priority, *pain_point, - best_choice_bundle.best_choice, &pending, - chunks_record, pain_points, blamer_bundle); + ProcessSegSearchPainPoint(pain_point_priority, pain_point, + LMPainPoints::PainPointDescription(pp_type), + &pending, word_res, &pain_points, blamer_bundle); - UpdateSegSearchNodes(pain_point->col, &pending, &best_path_by_column, - chunks_record, pain_points, &best_choice_bundle, + UpdateSegSearchNodes(rating_cert_scale, pain_point.col, &pending, + word_res, &pain_points, best_choice_bundle, blamer_bundle); - if (!best_choice_bundle.updated) ++num_futile_classifications; + if (!best_choice_bundle->updated) ++num_futile_classifications; if (segsearch_debug_level > 0) { tprintf("num_futile_classifications %d\n", num_futile_classifications); } - best_choice_bundle.updated = false; // reset updated - delete pain_point; // done using this pain point + best_choice_bundle->updated = false; // reset updated // See if it's time to terminate SegSearch or time for starting a guided // search for the true path to find the blame for the incorrect best_choice. - if (SegSearchDone(num_futile_classifications) && blamer_bundle != NULL && - blamer_bundle->incorrect_result_reason == IRR_CORRECT && - !blamer_bundle->segsearch_is_looking_for_blame && - blamer_bundle->truth_has_char_boxes && - !ChoiceIsCorrect(getDict().getUnicharset(), - best_choice, blamer_bundle->truth_text)) { - InitBlamerForSegSearch(best_choice_bundle.best_choice, chunks_record, - pain_points, blamer_bundle, &blamer_debug); + if (SegSearchDone(num_futile_classifications) && + blamer_bundle != NULL && + blamer_bundle->GuidedSegsearchNeeded(word_res->best_choice)) { + InitBlamerForSegSearch(word_res, &pain_points, blamer_bundle, + &blamer_debug); } } // end while loop exploring alternative paths - FinishBlamerForSegSearch(best_choice_bundle.best_choice, - blamer_bundle, &blamer_debug); + if (blamer_bundle != NULL) { + blamer_bundle->FinishSegSearch(word_res->best_choice, + wordrec_debug_blamer, &blamer_debug); + } if (segsearch_debug_level > 0) { tprintf("Done with SegSearch (AcceptableChoiceFound: %d)\n", language_model_->AcceptableChoiceFound()); } - - // Clean up. - FreeHeapData(pain_points, MATRIX_COORD::Delete); - delete[] best_path_by_column; - delete[] pending; - for (row = 0; row < ratings->dimension(); ++row) { - for (col = 0; col <= row; ++col) { - BLOB_CHOICE_LIST *rating = ratings->get(col, row); - if (rating != NOT_CLASSIFIED) language_model_->DeleteState(rating); - } - } } void Wordrec::UpdateSegSearchNodes( + float rating_cert_scale, int starting_col, - SEG_SEARCH_PENDING_LIST *pending[], - BestPathByColumn *best_path_by_column[], - CHUNKS_RECORD *chunks_record, - HEAP *pain_points, + GenericVector* pending, + WERD_RES *word_res, + LMPainPoints *pain_points, BestChoiceBundle *best_choice_bundle, BlamerBundle *blamer_bundle) { - MATRIX *ratings = chunks_record->ratings; + MATRIX *ratings = word_res->ratings; + ASSERT_HOST(ratings->dimension() == pending->size()); + ASSERT_HOST(ratings->dimension() == best_choice_bundle->beam.size()); for (int col = starting_col; col < ratings->dimension(); ++col) { + if (!(*pending)[col].WorkToDo()) continue; + int first_row = col; + int last_row = MIN(ratings->dimension() - 1, + col + ratings->bandwidth() - 1); + if ((*pending)[col].SingleRow() >= 0) { + first_row = last_row = (*pending)[col].SingleRow(); + } if (segsearch_debug_level > 0) { - tprintf("\n\nUpdateSegSearchNodes: evaluate children in col=%d\n", col); + tprintf("\n\nUpdateSegSearchNodes: col=%d, rows=[%d,%d], alljust=%d\n", + col, first_row, last_row, + (*pending)[col].IsRowJustClassified(MAX_INT32)); } // Iterate over the pending list for this column. - SEG_SEARCH_PENDING_LIST *pending_list = &((*pending)[col]); - SEG_SEARCH_PENDING_IT pending_it(pending_list); - GenericVector non_empty_rows; - while (!pending_it.empty()) { + for (int row = first_row; row <= last_row; ++row) { // Update language model state of this child+parent pair. - SEG_SEARCH_PENDING *p = pending_it.extract(); - if (non_empty_rows.length() == 0 || - non_empty_rows[non_empty_rows.length()-1] != p->child_row) { - non_empty_rows.push_back(p->child_row); - } - BLOB_CHOICE_LIST *current_node = ratings->get(col, p->child_row); - LanguageModelFlagsType new_changed = - language_model_->UpdateState(p->changed, col, p->child_row, - current_node, p->parent, pain_points, - best_path_by_column, chunks_record, - best_choice_bundle, blamer_bundle); - if (new_changed) { - // Since the language model state of this entry changed, add all the - // pairs with it as a parent and each of its children to pending, so - // that the children are updated as well. - int child_col = p->child_row + 1; - for (int child_row = child_col; - child_row < ratings->dimension(); ++child_row) { - if (ratings->get(child_col, child_row) != NOT_CLASSIFIED) { - SEG_SEARCH_PENDING *new_pending = - new SEG_SEARCH_PENDING(child_row, current_node, 0); - SEG_SEARCH_PENDING *actual_new_pending = - reinterpret_cast( - (*pending)[child_col].add_sorted_and_find( - SEG_SEARCH_PENDING::compare, true, new_pending)); - if (new_pending != actual_new_pending) delete new_pending; - actual_new_pending->changed |= new_changed; - if (segsearch_debug_level > 0) { - tprintf("Added child(col=%d row=%d) parent(col=%d row=%d)" - " changed=0x%x to pending\n", child_col, - actual_new_pending->child_row, - col, p->child_row, actual_new_pending->changed); - } - } + BLOB_CHOICE_LIST *current_node = ratings->get(col, row); + LanguageModelState *parent_node = + col == 0 ? NULL : best_choice_bundle->beam[col - 1]; + if (current_node != NULL && + language_model_->UpdateState((*pending)[col].IsRowJustClassified(row), + col, row, current_node, parent_node, + pain_points, word_res, + best_choice_bundle, blamer_bundle) && + row + 1 < ratings->dimension()) { + // Since the language model state of this entry changed, process all + // the child column. + (*pending)[row + 1].RevisitWholeColumn(); + if (segsearch_debug_level > 0) { + tprintf("Added child col=%d to pending\n", row + 1); } - } // end if new_changed - delete p; // clean up - pending_it.forward(); - } // end while !pending_it.empty() - language_model_->GeneratePainPointsFromColumn( - col, non_empty_rows, best_choice_bundle->best_choice->certainty(), - pain_points, best_path_by_column, chunks_record); - } // end for col - - if (best_choice_bundle->updated) { - language_model_->GeneratePainPointsFromBestChoice( - pain_points, chunks_record, best_choice_bundle); + } // end if UpdateState. + } // end for row. + } // end for col. + if (best_choice_bundle->best_vse != NULL) { + ASSERT_HOST(word_res->StatesAllValid()); + if (best_choice_bundle->best_vse->updated) { + pain_points->GenerateFromPath(rating_cert_scale, + best_choice_bundle->best_vse, word_res); + if (!best_choice_bundle->fixpt.empty()) { + pain_points->GenerateFromAmbigs(best_choice_bundle->fixpt, + best_choice_bundle->best_vse, word_res); + } + } + } + // The segsearch is completed. Reset all updated flags on all VSEs and reset + // all pendings. + for (int col = 0; col < pending->size(); ++col) { + (*pending)[col].Clear(); + ViterbiStateEntry_IT + vse_it(&best_choice_bundle->beam[col]->viterbi_state_entries); + for (vse_it.mark_cycle_pt(); !vse_it.cycled_list(); vse_it.forward()) { + vse_it.data()->updated = false; + } } - - language_model_->CleanUp(); } -void Wordrec::ProcessSegSearchPainPoint(float pain_point_priority, - const MATRIX_COORD &pain_point, - const WERD_CHOICE *best_choice, - SEG_SEARCH_PENDING_LIST *pending[], - CHUNKS_RECORD *chunks_record, - HEAP *pain_points, - BlamerBundle *blamer_bundle) { +void Wordrec::ProcessSegSearchPainPoint( + float pain_point_priority, + const MATRIX_COORD &pain_point, const char* pain_point_type, + GenericVector* pending, WERD_RES *word_res, + LMPainPoints *pain_points, BlamerBundle *blamer_bundle) { if (segsearch_debug_level > 0) { - tprintf("Classifying pain point priority=%.4f, col=%d, row=%d\n", - pain_point_priority, pain_point.col, pain_point.row); + tprintf("Classifying pain point %s priority=%.4f, col=%d, row=%d\n", + pain_point_type, pain_point_priority, + pain_point.col, pain_point.row); + } + ASSERT_HOST(pain_points != NULL); + MATRIX *ratings = word_res->ratings; + // Classify blob [pain_point.col pain_point.row] + if (!pain_point.Valid(*ratings)) { + ratings->IncreaseBandSize(pain_point.row + 1 - pain_point.col); + } + ASSERT_HOST(pain_point.Valid(*ratings)); + BLOB_CHOICE_LIST *classified = classify_piece(word_res->seam_array, + pain_point.col, pain_point.row, + pain_point_type, + word_res->chopped_word, + blamer_bundle); + BLOB_CHOICE_LIST *lst = ratings->get(pain_point.col, pain_point.row); + if (lst == NULL) { + ratings->put(pain_point.col, pain_point.row, classified); + } else { + // We can not delete old BLOB_CHOICEs, since they might contain + // ViterbiStateEntries that are parents of other "active" entries. + // Thus if the matrix cell already contains classifications we add + // the new ones to the beginning of the list. + BLOB_CHOICE_IT it(lst); + it.add_list_before(classified); + delete classified; // safe to delete, since empty after add_list_before() + classified = NULL; } - MATRIX *ratings = chunks_record->ratings; - BLOB_CHOICE_LIST *classified = classify_piece( - chunks_record->chunks, chunks_record->word_res->denorm, - chunks_record->splits, - pain_point.col, pain_point.row, blamer_bundle); - ratings->put(pain_point.col, pain_point.row, classified); if (segsearch_debug_level > 0) { print_ratings_list("Updated ratings matrix with a new entry:", @@ -281,143 +272,53 @@ void Wordrec::ProcessSegSearchPainPoint(float pain_point_priority, // Insert initial "pain points" to join the newly classified blob // with its left and right neighbors. - if (!classified->empty()) { - float worst_piece_cert; - bool fragmented; + if (classified != NULL && !classified->empty()) { if (pain_point.col > 0) { - language_model_->GetWorstPieceCertainty( - pain_point.col-1, pain_point.row, chunks_record->ratings, - &worst_piece_cert, &fragmented); - language_model_->GeneratePainPoint( - pain_point.col-1, pain_point.row, false, - LanguageModel::kInitialPainPointPriorityAdjustment, - worst_piece_cert, fragmented, best_choice->certainty(), - segsearch_max_char_wh_ratio, NULL, NULL, - chunks_record, pain_points); + pain_points->GeneratePainPoint( + pain_point.col - 1, pain_point.row, LM_PPTYPE_SHAPE, 0.0, + true, segsearch_max_char_wh_ratio, word_res); } - if (pain_point.row+1 < ratings->dimension()) { - language_model_->GetWorstPieceCertainty( - pain_point.col, pain_point.row+1, chunks_record->ratings, - &worst_piece_cert, &fragmented); - language_model_->GeneratePainPoint( - pain_point.col, pain_point.row+1, true, - LanguageModel::kInitialPainPointPriorityAdjustment, - worst_piece_cert, fragmented, best_choice->certainty(), - segsearch_max_char_wh_ratio, NULL, NULL, - chunks_record, pain_points); + if (pain_point.row + 1 < ratings->dimension()) { + pain_points->GeneratePainPoint( + pain_point.col, pain_point.row + 1, LM_PPTYPE_SHAPE, 0.0, + true, segsearch_max_char_wh_ratio, word_res); } } + (*pending)[pain_point.col].SetBlobClassified(pain_point.row); +} - // Record a pending entry with the pain_point and each of its parents. - int parent_row = pain_point.col - 1; - if (parent_row < 0) { // this node has no parents - (*pending)[pain_point.col].add_sorted( - SEG_SEARCH_PENDING::compare, true, - new SEG_SEARCH_PENDING(pain_point.row, NULL, - LanguageModel::kAllChangedFlag)); - } else { - for (int parent_col = 0; parent_col < pain_point.col; ++parent_col) { - if (ratings->get(parent_col, parent_row) != NOT_CLASSIFIED) { - (*pending)[pain_point.col].add_sorted( - SEG_SEARCH_PENDING::compare, true, - new SEG_SEARCH_PENDING(pain_point.row, - ratings->get(parent_col, parent_row), - LanguageModel::kAllChangedFlag)); - } - } +// Resets enough of the results so that the Viterbi search is re-run. +// Needed when the n-gram model is enabled, as the multi-length comparison +// implementation will re-value existing paths to worse values. +void Wordrec::ResetNGramSearch(WERD_RES* word_res, + BestChoiceBundle* best_choice_bundle, + GenericVector* pending) { + // TODO(rays) More refactoring required here. + // Delete existing viterbi states. + for (int col = 0; col < best_choice_bundle->beam.size(); ++col) { + best_choice_bundle->beam[col]->Clear(); } + // Reset best_choice_bundle. + word_res->ClearWordChoices(); + best_choice_bundle->best_vse = NULL; + // Clear out all existing pendings and add a new one for the first column. + (*pending)[0].SetColumnClassified(); + for (int i = 1; i < pending->size(); ++i) + (*pending)[i].Clear(); } -void Wordrec::InitBlamerForSegSearch(const WERD_CHOICE *best_choice, - CHUNKS_RECORD *chunks_record, - HEAP *pain_points, +void Wordrec::InitBlamerForSegSearch(WERD_RES *word_res, + LMPainPoints *pain_points, BlamerBundle *blamer_bundle, STRING *blamer_debug) { - blamer_bundle->segsearch_is_looking_for_blame = true; - if (wordrec_debug_blamer) { - tprintf("segsearch starting to look for blame\n"); - } - // Clear pain points heap. - int pop; - float pain_point_priority; - MATRIX_COORD *pain_point; - while ((pop = HeapPop(pain_points, &pain_point_priority, - &pain_point)) != EMPTY) { - delete pain_point; - } - // Fill pain points for any unclassifed blob corresponding to the - // correct segmentation state. - *blamer_debug += "Correct segmentation:\n"; - for (int idx = 0; - idx < blamer_bundle->correct_segmentation_cols.length(); ++idx) { - blamer_debug->add_str_int( - "col=", blamer_bundle->correct_segmentation_cols[idx]); - blamer_debug->add_str_int( - " row=", blamer_bundle->correct_segmentation_rows[idx]); - *blamer_debug += "\n"; - if (chunks_record->ratings->get( - blamer_bundle->correct_segmentation_cols[idx], - blamer_bundle->correct_segmentation_rows[idx]) == NOT_CLASSIFIED) { - if (!language_model_->GeneratePainPoint( - blamer_bundle->correct_segmentation_cols[idx], - blamer_bundle->correct_segmentation_rows[idx], - false, -1.0, -1.0, false, -1.0, segsearch_max_char_wh_ratio, - NULL, NULL, chunks_record, pain_points)) { - blamer_bundle->segsearch_is_looking_for_blame = false; - *blamer_debug += "\nFailed to insert pain point\n"; - blamer_bundle->SetBlame(IRR_SEGSEARCH_HEUR, *blamer_debug, best_choice, - wordrec_debug_blamer); - break; - } - } - } // end for blamer_bundle->correct_segmentation_cols/rows -} - -void Wordrec::FinishBlamerForSegSearch(const WERD_CHOICE *best_choice, - BlamerBundle *blamer_bundle, - STRING *blamer_debug) { - // If we are still looking for blame (i.e. best_choice is incorrect, but a - // path representing the correct segmentation could be constructed), we can - // blame segmentation search pain point prioritization if the rating of the - // path corresponding to the correct segmentation is better than that of - // best_choice (i.e. language model would have done the correct thing, but - // because of poor pain point prioritization the correct segmentation was - // never explored). Otherwise we blame the tradeoff between the language model - // and the classifier, since even after exploring the path corresponding to - // the correct segmentation incorrect best_choice would have been chosen. - // One special case when we blame the classifier instead is when best choice - // is incorrect, but it is a dictionary word and it classifier's top choice. - if (blamer_bundle != NULL && blamer_bundle->segsearch_is_looking_for_blame) { - blamer_bundle->segsearch_is_looking_for_blame = false; - if (blamer_bundle->best_choice_is_dict_and_top_choice) { - *blamer_debug = "Best choice is: incorrect, top choice, dictionary word"; - *blamer_debug += " with permuter "; - *blamer_debug += best_choice->permuter_name(); - blamer_bundle->SetBlame(IRR_CLASSIFIER, *blamer_debug, best_choice, - wordrec_debug_blamer); - } else if (blamer_bundle->best_correctly_segmented_rating < - best_choice->rating()) { - *blamer_debug += "Correct segmentation state was not explored"; - blamer_bundle->SetBlame(IRR_SEGSEARCH_PP, *blamer_debug, best_choice, - wordrec_debug_blamer); - } else { - if (blamer_bundle->best_correctly_segmented_rating >= - WERD_CHOICE::kBadRating) { - *blamer_debug += "Correct segmentation paths were pruned by LM\n"; - } else { - char debug_buffer[256]; - *blamer_debug += "Best correct segmentation rating "; - sprintf(debug_buffer, "%g", - blamer_bundle->best_correctly_segmented_rating); - *blamer_debug += debug_buffer; - *blamer_debug += " vs. best choice rating "; - sprintf(debug_buffer, "%g", best_choice->rating()); - *blamer_debug += debug_buffer; - } - blamer_bundle->SetBlame(IRR_CLASS_LM_TRADEOFF, *blamer_debug, best_choice, - wordrec_debug_blamer); - } - } + pain_points->Clear(); // Clear pain points heap. + TessResultCallback2* pp_cb = NewPermanentTessCallback( + pain_points, &LMPainPoints::GenerateForBlamer, + static_cast(segsearch_max_char_wh_ratio), word_res); + blamer_bundle->InitForSegSearch(word_res->best_choice, word_res->ratings, + getDict().WildcardID(), wordrec_debug_blamer, + blamer_debug, pp_cb); + delete pp_cb; } } // namespace tesseract diff --git a/wordrec/tface.cpp b/wordrec/tface.cpp index 709d0e0a6d..de6db3f4c0 100644 --- a/wordrec/tface.cpp +++ b/wordrec/tface.cpp @@ -17,7 +17,6 @@ * **********************************************************************/ -#include "bestfirst.h" #include "callcpp.h" #include "chop.h" #include "chopper.h" @@ -25,12 +24,10 @@ #include "fxdefs.h" #include "globals.h" #include "gradechop.h" -#include "matchtab.h" #include "pageres.h" -#include "permute.h" -#include "wordclass.h" #include "wordrec.h" #include "featdefs.h" +#include "params_model.h" #include #ifdef __UNIX__ @@ -54,9 +51,8 @@ void Wordrec::program_editup(const char *textbase, InitFeatureDefs(&feature_defs_); SetupExtractors(&feature_defs_); InitAdaptiveClassifier(init_classifier); - if (init_dict) getDict().Load(); + if (init_dict) getDict().Load(Dict::GlobalDawgCache()); pass2_ok_split = chop_ok_split; - pass2_seg_states = wordrec_num_seg_states; } /** @@ -79,8 +75,6 @@ int Wordrec::end_recog() { */ void Wordrec::program_editdown(inT32 elasped_time) { EndAdaptiveClassifier(); - blob_match_table.end_match_table(); - getDict().InitChoiceAccum(); getDict().End(); } @@ -92,7 +86,7 @@ void Wordrec::program_editdown(inT32 elasped_time) { */ void Wordrec::set_pass1() { chop_ok_split.set_value(70.0); - wordrec_num_seg_states.set_value(15); + language_model_->getParamsModel().SetPass(ParamsModel::PTRAIN_PASS1); SettupPass1(); } @@ -104,7 +98,7 @@ void Wordrec::set_pass1() { */ void Wordrec::set_pass2() { chop_ok_split.set_value(pass2_ok_split); - wordrec_num_seg_states.set_value(pass2_seg_states); + language_model_->getParamsModel().SetPass(ParamsModel::PTRAIN_PASS2); SettupPass2(); } @@ -114,13 +108,12 @@ void Wordrec::set_pass2() { * * Recognize a word. */ -BLOB_CHOICE_LIST_VECTOR *Wordrec::cc_recog(WERD_RES *word) { - getDict().InitChoiceAccum(); +void Wordrec::cc_recog(WERD_RES *word) { getDict().reset_hyphen_vars(word->word->flag(W_EOL)); - blob_match_table.init_match_table(); - BLOB_CHOICE_LIST_VECTOR *results = chop_word_main(word); - getDict().DebugWordChoices(); - return results; + chop_word_main(word); + word->DebugWordChoices(getDict().stopper_debug_level >= 1, + getDict().word_to_debug.string()); + ASSERT_HOST(word->StatesAllValid()); } @@ -140,17 +133,16 @@ int Wordrec::dict_word(const WERD_CHOICE &word) { * Called from Tess with a blob in tess form. * The blob may need rotating to the correct orientation for classification. */ -BLOB_CHOICE_LIST *Wordrec::call_matcher(const DENORM* denorm, TBLOB *tessblob) { +BLOB_CHOICE_LIST *Wordrec::call_matcher(TBLOB *tessblob) { // Rotate the blob for classification if necessary. - TBLOB* rotated_blob = tessblob->ClassifyNormalizeIfNeeded(&denorm); + TBLOB* rotated_blob = tessblob->ClassifyNormalizeIfNeeded(); if (rotated_blob == NULL) { rotated_blob = tessblob; } BLOB_CHOICE_LIST *ratings = new BLOB_CHOICE_LIST(); // matcher result - AdaptiveClassifier(rotated_blob, *denorm, ratings, NULL); + AdaptiveClassifier(rotated_blob, ratings, NULL); if (rotated_blob != tessblob) { delete rotated_blob; - delete denorm; } return ratings; } diff --git a/wordrec/wordclass.cpp b/wordrec/wordclass.cpp index 605694e5f9..6c8c33f556 100644 --- a/wordrec/wordclass.cpp +++ b/wordrec/wordclass.cpp @@ -25,18 +25,12 @@ /*---------------------------------------------------------------------- I N C L U D E S ----------------------------------------------------------------------*/ -#include -#ifdef __UNIX__ #include -#endif +#include -#include "wordclass.h" #include "associate.h" #include "render.h" -#include "matchtab.h" -#include "permute.h" #include "callcpp.h" -#include #include "wordrec.h" // Include automatically generated configuration file if running autoconf. @@ -59,69 +53,25 @@ namespace tesseract { * @param string The string to display in ScrollView * @param color The colour to use when displayed with ScrollView */ -BLOB_CHOICE_LIST *Wordrec::classify_blob(TBLOB *blob, const DENORM& denorm, +BLOB_CHOICE_LIST *Wordrec::classify_blob(TBLOB *blob, const char *string, C_COL color, BlamerBundle *blamer_bundle) { - fflush(stdout); - BLOB_CHOICE_LIST *choices = NULL; #ifndef GRAPHICS_DISABLED if (wordrec_display_all_blobs) display_blob(blob, color); #endif - choices = blob_match_table.get_match(blob); - if (choices == NULL) { - choices = call_matcher(&denorm, blob); - blob_match_table.put_match(blob, choices); - // If a blob with the same bounding box as one of the truth character - // bounding boxes is not classified as the corresponding truth character - // blame character classifier for incorrect answer. - if (blamer_bundle != NULL && blamer_bundle->truth_has_char_boxes && - blamer_bundle->incorrect_result_reason == IRR_CORRECT) { - for (int b = 0; b < blamer_bundle->norm_truth_word.length(); ++b) { - const TBOX &truth_box = blamer_bundle->norm_truth_word.BlobBox(b); - const TBOX &blob_box = blob->bounding_box(); - // Note that we are more strict on the bounding box boundaries here - // than in other places (chopper, segmentation search), since we do - // not have the ability to check the previous and next bounding box. - if (blob_box.x_almost_equal(truth_box, - blamer_bundle->norm_box_tolerance/2)) { - BLOB_CHOICE_IT choices_it(choices); - bool found = false; - bool incorrect_adapted = false; - UNICHAR_ID incorrect_adapted_id = INVALID_UNICHAR_ID; - const char *truth_str = blamer_bundle->truth_text[b].string(); - for (choices_it.mark_cycle_pt(); !choices_it.cycled_list(); - choices_it.forward()) { - if (strcmp(truth_str, getDict().getUnicharset().get_normed_unichar( - choices_it.data()->unichar_id())) == 0) { - found = true; - break; - } else if (choices_it.data()->adapted()) { - incorrect_adapted = true; - incorrect_adapted_id = choices_it.data()->unichar_id(); - } - } // end choices_it for loop - if (!found) { - STRING debug = "unichar "; - debug += truth_str; - debug += " not found in classification list"; - blamer_bundle->SetBlame(IRR_CLASSIFIER, debug, - NULL, wordrec_debug_blamer); - } else if (incorrect_adapted) { - STRING debug = "better rating for adapted "; - debug += getDict().getUnicharset().id_to_unichar( - incorrect_adapted_id); - debug += " than for correct "; - debug += truth_str; - blamer_bundle->SetBlame(IRR_ADAPTION, debug, - NULL, wordrec_debug_blamer); - } - break; - } - } // end iterating over blamer_bundle->norm_truth_word - } + // TODO(rays) collapse with call_matcher and move all to wordrec.cpp. + BLOB_CHOICE_LIST* choices = call_matcher(blob); + // If a blob with the same bounding box as one of the truth character + // bounding boxes is not classified as the corresponding truth character + // blame character classifier for incorrect answer. + if (blamer_bundle != NULL) { + blamer_bundle->BlameClassifier(getDict().getUnicharset(), + blob->bounding_box(), + *choices, + wordrec_debug_blamer); } -#ifndef GRAPHICS_DISABLED + #ifndef GRAPHICS_DISABLED if (classify_debug_level && string) print_ratings_list(string, choices, getDict().getUnicharset()); @@ -129,34 +79,7 @@ BLOB_CHOICE_LIST *Wordrec::classify_blob(TBLOB *blob, const DENORM& denorm, window_wait(blob_window); #endif - return (choices); -} - -// Returns a valid BLOB_CHOICE_LIST representing the given result. -BLOB_CHOICE_LIST *Wordrec::fake_classify_blob(UNICHAR_ID class_id, - float rating, float certainty) { - BLOB_CHOICE_LIST *ratings = new BLOB_CHOICE_LIST(); // matcher result - BLOB_CHOICE *choice = - new BLOB_CHOICE(class_id, rating, certainty, -1, -1, 0, 0, 0, false); - BLOB_CHOICE_IT temp_it(ratings); - temp_it.add_after_stay_put(choice); - return ratings; -} - -/** - * @name update_blob_classifications - * - * For each blob in the given word update match_table with the - * corresponding BLOB_CHOICES_LIST from choices. - */ -void Wordrec::update_blob_classifications( - TWERD *word, const BLOB_CHOICE_LIST_VECTOR &choices) { - TBLOB *tblob = word->blobs; - int index = 0; - for (; tblob != NULL && index < choices.length(); - tblob = tblob->next, index++) { - blob_match_table.add_to_match(tblob, choices.get(index)); - } + return choices; } } // namespace tesseract; diff --git a/wordrec/wordclass.h b/wordrec/wordclass.h deleted file mode 100644 index e5fd7e9457..0000000000 --- a/wordrec/wordclass.h +++ /dev/null @@ -1,38 +0,0 @@ -/* -*-C-*- - ******************************************************************************** - * - * File: wordclass.h - * Description: Word level classifier - * Author: Mark Seaman, OCR Technology - * Created: Mon Feb 5 11:42:51 1990 - * Modified: Thu Apr 18 14:33:24 1991 (Mark Seaman) marks@hpgrlt - * Language: C - * Package: N/A - * Status: Experimental (Do Not Distribute) - * - * (c) Copyright 1990, Hewlett-Packard Company. - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** http://www.apache.org/licenses/LICENSE-2.0 - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - * - *********************************************************************************/ -#ifndef WERDCLASSH -#define WERDCLASSH - -#include "blobs.h" -#include "callcpp.h" -#include "ratngs.h" -#include "states.h" - -void write_text_files(TWERD *word, - char *raw_choice, - int same_row, - int good_word, - int firstpass); -#endif diff --git a/wordrec/wordrec.cpp b/wordrec/wordrec.cpp index ef77e08a71..6ca808740e 100644 --- a/wordrec/wordrec.cpp +++ b/wordrec/wordrec.cpp @@ -36,8 +36,6 @@ Wordrec::Wordrec() : "force associator to run regardless of what enable_assoc is." "This is used for CJK where component grouping is necessary.", CCUtil::params()), - INT_MEMBER(wordrec_num_seg_states, 30, "Segmentation states", - CCUtil::params()), double_MEMBER(wordrec_worst_state, 1.0, "Worst segmentation state", params()), BOOL_MEMBER(fragments_guide_chopper, FALSE, @@ -69,6 +67,9 @@ Wordrec::Wordrec() : params()), double_MEMBER(chop_center_knob, 0.15, "Split center adjustment", params()), + INT_MEMBER(chop_centered_maxwidth, 90, "Width of (smaller) chopped blobs " + "above which we don't care that a chop is not near the center.", + params()), double_MEMBER(chop_sharpness_knob, 0.06, "Split sharpness adjustment", params()), double_MEMBER(chop_width_change_knob, 5.0, "Width change adjustment", @@ -84,60 +85,35 @@ Wordrec::Wordrec() : BOOL_MEMBER(assume_fixed_pitch_char_segment, FALSE, "include fixed-pitch heuristics in char segmentation", params()), - BOOL_MEMBER(use_new_state_cost, FALSE, - "use new state cost heuristics for segmentation state evaluation", - params()), - double_MEMBER(heuristic_segcost_rating_base, 1.25, - "base factor for adding segmentation cost into word rating." - "It's a multiplying factor, the larger the value above 1, " - "the bigger the effect of segmentation cost.", - params()), - double_MEMBER(heuristic_weight_rating, 1.0, - "weight associated with char rating in combined cost of state", - params()), - double_MEMBER(heuristic_weight_width, 1000.0, - "weight associated with width evidence in combined cost of" - " state", params()), - double_MEMBER(heuristic_weight_seamcut, 0.0, - "weight associated with seam cut in combined cost of state", - params()), - double_MEMBER(heuristic_max_char_wh_ratio, 2.0, - "max char width-to-height ratio allowed in segmentation", - params()), INT_MEMBER(wordrec_debug_level, 0, "Debug level for wordrec", params()), + INT_MEMBER(wordrec_max_join_chunks, 4, + "Max number of broken pieces to associate", params()), + BOOL_MEMBER(wordrec_skip_no_truth_words, false, + "Only run OCR for words that had truth recorded in BlamerBundle", + params()), BOOL_MEMBER(wordrec_debug_blamer, false, "Print blamer debug messages", params()), BOOL_MEMBER(wordrec_run_blamer, false, "Try to set the blame for errors", params()), - BOOL_MEMBER(enable_new_segsearch, true, - "Enable new segmentation search path.", params()), INT_MEMBER(segsearch_debug_level, 0, "SegSearch debug level", params()), INT_MEMBER(segsearch_max_pain_points, 2000, "Maximum number of pain points stored in the queue", params()), - INT_MEMBER(segsearch_max_futile_classifications, 10, - "Maximum number of pain point classifications per word that" + INT_MEMBER(segsearch_max_futile_classifications, 20, + "Maximum number of pain point classifications per chunk that" "did not result in finding a better word choice.", params()), double_MEMBER(segsearch_max_char_wh_ratio, 2.0, "Maximum character width-to-height ratio", params()), - double_MEMBER(segsearch_max_fixed_pitch_char_wh_ratio, 2.0, - "Maximum character width-to-height ratio for" - " fixed-pitch fonts", - params()), - BOOL_MEMBER(save_alt_choices, false, + BOOL_MEMBER(save_alt_choices, true, "Save alternative paths found during chopping" " and segmentation search", params()) { prev_word_best_choice_ = NULL; language_model_ = new LanguageModel(&get_fontinfo_table(), &(getDict())); - pass2_seg_states = 0; - num_joints = 0; - num_pushed = 0; - num_popped = 0; fill_lattice_ = NULL; } @@ -145,60 +121,4 @@ Wordrec::~Wordrec() { delete language_model_; } -void Wordrec::CopyCharChoices(const BLOB_CHOICE_LIST_VECTOR &from, - BLOB_CHOICE_LIST_VECTOR *to) { - to->delete_data_pointers(); - to->clear(); - for (int i = 0; i < from.size(); ++i) { - BLOB_CHOICE_LIST *cc_list = new BLOB_CHOICE_LIST(); - cc_list->deep_copy(from[i], &BLOB_CHOICE::deep_copy); - to->push_back(cc_list); - } -} - -bool Wordrec::ChoiceIsCorrect(const UNICHARSET &uni_set, - const WERD_CHOICE *choice, - const GenericVector &truth_text) { - if (choice == NULL) return false; - int i; - STRING truth_str; - for (i = 0; i < truth_text.length(); ++i) truth_str += truth_text[i]; - STRING normed_choice_str; - for (i = 0; i < choice->length(); ++i) { - normed_choice_str += uni_set.get_normed_unichar(choice->unichar_id(i)); - } - return (truth_str == normed_choice_str); -} - -void Wordrec::SaveAltChoices(const LIST &best_choices, WERD_RES *word) { - ASSERT_HOST(word->alt_choices.empty()); - ASSERT_HOST(word->alt_states.empty()); - LIST list_it; - iterate_list(list_it, best_choices) { - VIABLE_CHOICE choice = - reinterpret_cast(first_node(list_it)); - CHAR_CHOICE *char_choice = &(choice->Blob[0]); - WERD_CHOICE *alt_choice = new WERD_CHOICE(word->uch_set, choice->Length); - word->alt_states.push_back(GenericVector(choice->Length)); - GenericVector &alt_state = word->alt_states.back(); - for (int i = 0; i < choice->Length; char_choice++, i++) { - alt_choice->append_unichar_id_space_allocated( - char_choice->Class, 1, 0, 0); - alt_state.push_back(char_choice->NumChunks); - } - alt_choice->set_rating(choice->Rating); - alt_choice->set_certainty(choice->Certainty); - - ASSERT_HOST(choice->blob_choices != NULL); - alt_choice->set_blob_choices(choice->blob_choices); - choice->blob_choices = NULL; - - word->alt_choices.push_back(alt_choice); - if (wordrec_debug_level > 0) { - tprintf("SaveAltChoices: %s %g\n", - alt_choice->unichar_string().string(), alt_choice->rating()); - } - } -} - } // namespace tesseract diff --git a/wordrec/wordrec.h b/wordrec/wordrec.h index 4be5cba610..c049580ed3 100644 --- a/wordrec/wordrec.h +++ b/wordrec/wordrec.h @@ -25,51 +25,83 @@ #include "language_model.h" #include "ratngs.h" #include "matrix.h" -#include "matchtab.h" -#include "oldheap.h" #include "gradechop.h" #include "seam.h" -#include "states.h" #include "findseam.h" #include "callcpp.h" -struct CHUNKS_RECORD; -struct SEARCH_RECORD; class WERD_RES; -// A struct for storing child/parent pairs of the BLOB_CHOICE_LISTs -// to be processed by the segmentation search. -struct SEG_SEARCH_PENDING : public ELIST_LINK { - SEG_SEARCH_PENDING(int child_row_arg, - BLOB_CHOICE_LIST *parent_arg, - tesseract::LanguageModelFlagsType changed_arg) : - child_row(child_row_arg), parent(parent_arg), changed(changed_arg) {} - - // Comparator function for add_sorted(). - static int compare(const void *p1, const void *p2) { - const SEG_SEARCH_PENDING *e1 = *reinterpret_cast< - const SEG_SEARCH_PENDING * const *>(p1); - const SEG_SEARCH_PENDING *e2 = *reinterpret_cast< - const SEG_SEARCH_PENDING * const *>(p2); - if (e1->child_row == e2->child_row && - e1->parent == e2->parent) return 0; - return (e1->child_row < e2->child_row) ? -1 : 1; +namespace tesseract { + +// A class for storing which nodes are to be processed by the segmentation +// search. There is a single SegSearchPending for each column in the ratings +// matrix, and it indicates whether the segsearch should combine all +// BLOB_CHOICES in the column, or just the given row with the parents +// corresponding to *this SegSearchPending, and whether only updated parent +// ViterbiStateEntries should be combined, or all, with the BLOB_CHOICEs. +class SegSearchPending { + public: + SegSearchPending() + : classified_row_(-1), + revisit_whole_column_(false), + column_classified_(false) {} + + // Marks the whole column as just classified. Used to start a search on + // a newly initialized ratings matrix. + void SetColumnClassified() { + column_classified_ = true; + } + // Marks the matrix entry at the given row as just classified. + // Used after classifying a new matrix cell. + // Additional to, not overriding a previous RevisitWholeColumn. + void SetBlobClassified(int row) { + classified_row_ = row; + } + // Marks the whole column as needing work, but not just classified. + // Used when the parent vse list is updated. + // Additional to, not overriding a previous SetBlobClassified. + void RevisitWholeColumn() { + revisit_whole_column_ = true; } - int child_row; // row of the child in the ratings matrix - BLOB_CHOICE_LIST *parent; // pointer to the parent BLOB_CHOICE_LIST - // Flags that indicate which language model components are still active - // on the parent path (i.e. recorded some changes to the language model - // state) and need to be invoked for this pending entry. - // This field is used as an argument to LanguageModel::UpdateState() - // in Wordrec::UpdateSegSearchNodes(). - tesseract::LanguageModelFlagsType changed; -}; + // Clears *this to indicate no work to do. + void Clear() { + classified_row_ = -1; + revisit_whole_column_ = false; + column_classified_ = false; + } -ELISTIZEH(SEG_SEARCH_PENDING); + // Returns true if there are updates to do in the column that *this + // represents. + bool WorkToDo() const { + return revisit_whole_column_ || column_classified_ || classified_row_ >= 0; + } + // Returns true if the given row was just classified. + bool IsRowJustClassified(int row) const { + return row == classified_row_ || column_classified_; + } + // Returns the single row to process if there is only one, otherwise -1. + int SingleRow() const { + return revisit_whole_column_ || column_classified_ ? -1 : classified_row_; + } + private: + // If non-negative, indicates the single row in the ratings matrix that has + // just been classified, and so should be combined with all the parents in the + // column that this SegSearchPending represents. + // Operates independently of revisit_whole_column. + int classified_row_; + // If revisit_whole_column is true, then all BLOB_CHOICEs in this column will + // be processed, but classified_row can indicate a row that is newly + // classified. Overridden if column_classified is true. + bool revisit_whole_column_; + // If column_classified is true, parent vses are processed with all rows + // regardless of whether they are just updated, overriding + // revisit_whole_column and classified_row. + bool column_classified_; +}; -namespace tesseract { /* ccmain/tstruct.cpp *********************************************************/ class FRAGMENT:public ELIST_LINK @@ -99,7 +131,6 @@ class Wordrec : public Classify { BOOL_VAR_H(force_word_assoc, FALSE, "force associator to run regardless of what enable_assoc is." "This is used for CJK where component grouping is necessary."); - INT_VAR_H(wordrec_num_seg_states, 30, "Segmentation states"); double_VAR_H(wordrec_worst_state, 1, "Worst segmentation state"); BOOL_VAR_H(fragments_guide_chopper, FALSE, "Use information from fragments to guide chopping process"); @@ -116,6 +147,8 @@ class Wordrec : public Classify { double_VAR_H(chop_split_dist_knob, 0.5, "Split length adjustment"); double_VAR_H(chop_overlap_knob, 0.9, "Split overlap adjustment"); double_VAR_H(chop_center_knob, 0.15, "Split center adjustment"); + INT_VAR_H(chop_centered_maxwidth, 90, "Width of (smaller) chopped blobs " + "above which we don't care that a chop is not near the center."); double_VAR_H(chop_sharpness_knob, 0.06, "Split sharpness adjustment"); double_VAR_H(chop_width_change_knob, 5.0, "Width change adjustment"); double_VAR_H(chop_ok_split, 100.0, "OK split limit"); @@ -124,25 +157,13 @@ class Wordrec : public Classify { INT_VAR_H(segment_adjust_debug, 0, "Segmentation adjustment debug"); BOOL_VAR_H(assume_fixed_pitch_char_segment, FALSE, "include fixed-pitch heuristics in char segmentation"); - BOOL_VAR_H(use_new_state_cost, FALSE, - "use new state cost heuristics for segmentation state evaluation"); - double_VAR_H(heuristic_segcost_rating_base, 1.25, - "base factor for adding segmentation cost into word rating." - "It's a multiplying factor, the larger the value above 1, " - "the bigger the effect of segmentation cost."); - double_VAR_H(heuristic_weight_rating, 1, - "weight associated with char rating in combined cost of state"); - double_VAR_H(heuristic_weight_width, 0, - "weight associated with width evidence in combined cost of state"); - double_VAR_H(heuristic_weight_seamcut, 0, - "weight associated with seam cut in combined cost of state"); - double_VAR_H(heuristic_max_char_wh_ratio, 2.0, - "max char width-to-height ratio allowed in segmentation"); INT_VAR_H(wordrec_debug_level, 0, "Debug level for wordrec"); + INT_VAR_H(wordrec_max_join_chunks, 4, + "Max number of broken pieces to associate"); + BOOL_VAR_H(wordrec_skip_no_truth_words, false, + "Only run OCR for words that had truth recorded in BlamerBundle"); BOOL_VAR_H(wordrec_debug_blamer, false, "Print blamer debug messages"); BOOL_VAR_H(wordrec_run_blamer, false, "Try to set the blame for errors"); - BOOL_VAR_H(enable_new_segsearch, false, - "Enable new segmentation search path."); INT_VAR_H(segsearch_debug_level, 0, "SegSearch debug level"); INT_VAR_H(segsearch_max_pain_points, 2000, "Maximum number of pain points stored in the queue"); @@ -150,10 +171,7 @@ class Wordrec : public Classify { "Maximum number of pain point classifications per word."); double_VAR_H(segsearch_max_char_wh_ratio, 2.0, "Maximum character width-to-height ratio"); - double_VAR_H(segsearch_max_fixed_pitch_char_wh_ratio, 2.0, - "Maximum character width-to-height ratio for" - "fixed pitch fonts"); - BOOL_VAR_H(save_alt_choices, false, + BOOL_VAR_H(save_alt_choices, true, "Save alternative paths found during chopping " "and segmentation search"); @@ -161,27 +179,21 @@ class Wordrec : public Classify { Wordrec(); virtual ~Wordrec(); - void CopyCharChoices(const BLOB_CHOICE_LIST_VECTOR &from, - BLOB_CHOICE_LIST_VECTOR *to); - - // Returns true if text recorded in choice is the same as truth_text. - bool ChoiceIsCorrect(const UNICHARSET& uni_set, - const WERD_CHOICE *choice, - const GenericVector &truth_text); - // Fills word->alt_choices with alternative paths found during // chopping/segmentation search that are kept in best_choices. void SaveAltChoices(const LIST &best_choices, WERD_RES *word); // Fills character choice lattice in the given BlamerBundle // using the given ratings matrix and best choice list. - void FillLattice(const MATRIX &ratings, const LIST &best_choices, + void FillLattice(const MATRIX &ratings, const WERD_CHOICE_LIST &best_choices, const UNICHARSET &unicharset, BlamerBundle *blamer_bundle); // Calls fill_lattice_ member function // (assumes that fill_lattice_ is not NULL). - void CallFillLattice(const MATRIX &ratings, const LIST &best_choices, - const UNICHARSET &unicharset, BlamerBundle *blamer_bundle) { + void CallFillLattice(const MATRIX &ratings, + const WERD_CHOICE_LIST &best_choices, + const UNICHARSET &unicharset, + BlamerBundle *blamer_bundle) { (this->*fill_lattice_)(ratings, best_choices, unicharset, blamer_bundle); } @@ -189,81 +201,18 @@ class Wordrec : public Classify { void program_editup(const char *textbase, bool init_classifier, bool init_permute); - BLOB_CHOICE_LIST_VECTOR *cc_recog(WERD_RES *word); + void cc_recog(WERD_RES *word); void program_editdown(inT32 elasped_time); void set_pass1(); void set_pass2(); int end_recog(); - BLOB_CHOICE_LIST *call_matcher(const DENORM* denorm, TBLOB* blob); + BLOB_CHOICE_LIST *call_matcher(TBLOB* blob); int dict_word(const WERD_CHOICE &word); // wordclass.cpp BLOB_CHOICE_LIST *classify_blob(TBLOB *blob, - const DENORM& denorm, const char *string, C_COL color, BlamerBundle *blamer_bundle); - BLOB_CHOICE_LIST *fake_classify_blob(UNICHAR_ID class_id, - float rating, float certainty); - void update_blob_classifications(TWERD *word, - const BLOB_CHOICE_LIST_VECTOR &choices); - - // bestfirst.cpp - BLOB_CHOICE_LIST_VECTOR *evaluate_chunks(CHUNKS_RECORD *chunks_record, - SEARCH_STATE search_state, - BlamerBundle *blamer_bundle); - void update_ratings(const BLOB_CHOICE_LIST_VECTOR &new_choices, - const CHUNKS_RECORD *chunks_record, - const SEARCH_STATE search_state); - inT16 evaluate_state(CHUNKS_RECORD *chunks_record, - SEARCH_RECORD *the_search, - DANGERR *fixpt, - BlamerBundle *blamer_bundle); - SEARCH_RECORD *new_search(CHUNKS_RECORD *chunks_record, - int num_joints, - BLOB_CHOICE_LIST_VECTOR *best_char_choices, - WERD_CHOICE *best_choice, - WERD_CHOICE *raw_choice, - STATE *state); - void best_first_search(CHUNKS_RECORD *chunks_record, - BLOB_CHOICE_LIST_VECTOR *best_char_choices, - WERD_RES *word, - STATE *state, - DANGERR *fixpt, - STATE *best_state); - void delete_search(SEARCH_RECORD *the_search); - void expand_node(FLOAT32 worst_priority, - CHUNKS_RECORD *chunks_record, - SEARCH_RECORD *the_search); - void replace_char_widths(CHUNKS_RECORD *chunks_record, - SEARCH_STATE state); - // Transfers the given state to the word's output fields: rebuild_word, - // best_state, box_word, and returns the corresponding blob choices. - BLOB_CHOICE_LIST_VECTOR *rebuild_current_state( - WERD_RES *word, - STATE *state, - BLOB_CHOICE_LIST_VECTOR *char_choices, - MATRIX *ratings); - // Creates a fake blob choice from the combination of the given fragments. - // unichar is the class to be made from the combination, - // expanded_fragment_lengths[choice_index] is the number of fragments to use. - // old_choices[choice_index] has the classifier output for each fragment. - // choice index initially indexes the last fragment and should be decremented - // expanded_fragment_lengths[choice_index] times to get the earlier fragments. - // Guarantees to return something non-null, or abort! - BLOB_CHOICE* rebuild_fragments( - const char* unichar, - const char* expanded_fragment_lengths, - int choice_index, - BLOB_CHOICE_LIST_VECTOR *old_choices); - // Creates a joined copy of the blobs between x and y (inclusive) and - // insert into the rebuild_word in word. - // Returns a deep copy of the classifier results for the blob. - BLOB_CHOICE_LIST *join_blobs_and_classify( - WERD_RES* word, int x, int y, int choice_index, MATRIX *ratings, - BLOB_CHOICE_LIST_VECTOR *old_choices); - STATE *pop_queue(HEAP *queue); - void push_queue(HEAP *queue, STATE *state, FLOAT32 worst_priority, - FLOAT32 priority, bool debug); // segsearch.cpp // SegSearch works on the lower diagonal matrix of BLOB_CHOICE_LISTs. @@ -310,94 +259,91 @@ class Wordrec : public Classify { // composed from the best paths in the segmentation graph is "good enough" // (e.g. above a certain certainty threshold, is an unambiguous dictionary // word, etc) or there are no more "pain points" to explore. - void SegSearch(CHUNKS_RECORD *chunks_record, - WERD_CHOICE *best_choice, - BLOB_CHOICE_LIST_VECTOR *best_char_choices, - WERD_CHOICE *raw_choice, - STATE *output_best_state, - BlamerBundle *blamer_bundle); + // + // If associate_blobs is set to false no new classifications will be done + // to combine blobs. Segmentation search will run only one "iteration" + // on the classifications already recorded in chunks_record.ratings. + // + // Note: this function assumes that word, output_best_state, + // best_char_choices and fixpt arguments are not NULL. + void SegSearch(WERD_RES* word_res, + BestChoiceBundle* best_choice_bundle, + BlamerBundle* blamer_bundle); + + // Runs SegSearch() function (above) without needing a best_choice_bundle + // or blamer_bundle. Used for testing. + void DoSegSearch(WERD_RES* word_res); // chop.cpp PRIORITY point_priority(EDGEPT *point); - void add_point_to_list(POINT_GROUP point_list, EDGEPT *point); + void add_point_to_list(PointHeap* point_heap, EDGEPT *point); int angle_change(EDGEPT *point1, EDGEPT *point2, EDGEPT *point3); int is_little_chunk(EDGEPT *point1, EDGEPT *point2); int is_small_area(EDGEPT *point1, EDGEPT *point2); EDGEPT *pick_close_point(EDGEPT *critical_point, EDGEPT *vertical_point, int *best_dist); - void prioritize_points(TESSLINE *outline, POINT_GROUP points); - void new_min_point(EDGEPT *local_min, POINT_GROUP points); - void new_max_point(EDGEPT *local_max, POINT_GROUP points); + void prioritize_points(TESSLINE *outline, PointHeap* points); + void new_min_point(EDGEPT *local_min, PointHeap* points); + void new_max_point(EDGEPT *local_max, PointHeap* points); void vertical_projection_point(EDGEPT *split_point, EDGEPT *target_point, EDGEPT** best_point, EDGEPT_CLIST *new_points); // chopper.cpp SEAM *attempt_blob_chop(TWERD *word, TBLOB *blob, inT32 blob_number, - bool italic_blob, SEAMS seam_list); + bool italic_blob, const GenericVector& seams); SEAM *chop_numbered_blob(TWERD *word, inT32 blob_number, - bool italic_blob, SEAMS seam_list); + bool italic_blob, const GenericVector& seams); SEAM *chop_overlapping_blob(const GenericVector& boxes, - WERD_RES *word_res, inT32 *blob_number, - bool italic_blob, SEAMS seam_list); - bool improve_one_blob(WERD_RES *word_res, - BLOB_CHOICE_LIST_VECTOR *char_choices, - inT32 *blob_number, - SEAMS *seam_list, - DANGERR *fixpt, - bool split_next_to_fragment, - BlamerBundle *blamer_bundle); - void modify_blob_choice(BLOB_CHOICE_LIST *answer, - int chop_index); - bool chop_one_blob(TWERD *word, - BLOB_CHOICE_LIST_VECTOR *char_choices, - inT32 *blob_number, - SEAMS *seam_list, - int *right_chop_index); - bool chop_one_blob2(const GenericVector& boxes, - WERD_RES *word_res, SEAMS *seam_list); - BLOB_CHOICE_LIST_VECTOR *chop_word_main(WERD_RES *word); - void improve_by_chopping(WERD_RES *word, - BLOB_CHOICE_LIST_VECTOR *char_choices, - STATE *best_state, - BLOB_CHOICE_LIST_VECTOR *best_char_choices, - DANGERR *fixpt, - bool *updated_best_choice); - MATRIX *word_associator(bool only_create_ratings_matrtix, - WERD_RES *word, - STATE *state, - BLOB_CHOICE_LIST_VECTOR *best_char_choices, - DANGERR *fixpt, - STATE *best_state); - inT16 select_blob_to_split(const BLOB_CHOICE_LIST_VECTOR &char_choices, - float rating_ceiling, - bool split_next_to_fragment); - inT16 select_blob_to_split_from_fixpt(DANGERR *fixpt); - void set_chopper_blame(WERD_RES *word); + bool italic_blob, + WERD_RES *word_res, int *blob_number); + SEAM *improve_one_blob(const GenericVector &blob_choices, + DANGERR *fixpt, + bool split_next_to_fragment, + bool italic_blob, + WERD_RES *word, + int *blob_number); + SEAM *chop_one_blob(const GenericVector &boxes, + const GenericVector &blob_choices, + WERD_RES *word_res, + int *blob_number); + void chop_word_main(WERD_RES *word); + void improve_by_chopping(float rating_cert_scale, + WERD_RES *word, + BestChoiceBundle *best_choice_bundle, + BlamerBundle *blamer_bundle, + LMPainPoints *pain_points, + GenericVector* pending); + int select_blob_to_split(const GenericVector &blob_choices, + float rating_ceiling, + bool split_next_to_fragment); + int select_blob_to_split_from_fixpt(DANGERR *fixpt); // findseam.cpp - void junk_worst_seam(SEAM_QUEUE seams, SEAM *new_seam, float new_priority); - void choose_best_seam(SEAM_QUEUE seam_queue, - SEAM_PILE *seam_pile, + void add_seam_to_queue(float new_priority, SEAM *new_seam, SeamQueue* seams); + void choose_best_seam(SeamQueue* seam_queue, SPLIT *split, PRIORITY priority, SEAM **seam_result, - TBLOB *blob); - void combine_seam(SEAM_QUEUE seam_queue, SEAM_PILE seam_pile, SEAM *seam); + TBLOB *blob, + GenericVector* seam_pile); + void combine_seam(const GenericVector& seam_pile, + const SEAM* seam, SeamQueue* seam_queue); inT16 constrained_split(SPLIT *split, TBLOB *blob); - void delete_seam_pile(SEAM_PILE seam_pile); SEAM *pick_good_seam(TBLOB *blob); PRIORITY seam_priority(SEAM *seam, inT16 xmin, inT16 xmax); void try_point_pairs (EDGEPT * points[MAX_NUM_POINTS], inT16 num_points, - SEAM_QUEUE seam_queue, - SEAM_PILE * seam_pile, SEAM ** seam, TBLOB * blob); + SeamQueue* seam_queue, + GenericVector* seam_pile, + SEAM ** seam, TBLOB * blob); void try_vertical_splits(EDGEPT * points[MAX_NUM_POINTS], inT16 num_points, EDGEPT_CLIST *new_points, - SEAM_QUEUE seam_queue, - SEAM_PILE * seam_pile, SEAM ** seam, TBLOB * blob); + SeamQueue* seam_queue, + GenericVector* seam_pile, + SEAM ** seam, TBLOB * blob); // gradechop.cpp PRIORITY full_split_priority(SPLIT *split, inT16 xmin, inT16 xmax); @@ -419,11 +365,11 @@ class Wordrec : public Classify { void reverse_outline(EDGEPT *outline); // pieces.cpp - virtual BLOB_CHOICE_LIST *classify_piece(TBLOB *pieces, - const DENORM& denorm, - SEAMS seams, + virtual BLOB_CHOICE_LIST *classify_piece(const GenericVector& seams, inT16 start, inT16 end, + const char* description, + TWERD *word, BlamerBundle *blamer_bundle); // Try to merge fragments in the ratings matrix and put the result in // the corresponding row and column @@ -461,45 +407,11 @@ class Wordrec : public Classify { int fragment_pos, int num_frag_parts, BLOB_CHOICE_LIST *filtered_choices); - BLOB_CHOICE_LIST *get_piece_rating(MATRIX *ratings, - TBLOB *blobs, - const DENORM& denorm, - SEAMS seams, - inT16 start, - inT16 end, - BlamerBundle *blamer_bundle); - // returns an array of bounding boxes for the given list of blobs. - TBOX *record_blob_bounds(TBLOB *blobs); - MATRIX *record_piece_ratings(TBLOB *blobs); - - // heuristic.cpp - WIDTH_RECORD* state_char_widths(WIDTH_RECORD *chunk_widths, - STATE *state, - int num_joints); - FLOAT32 get_width_variance(WIDTH_RECORD *wrec, float norm_height); - FLOAT32 get_gap_variance(WIDTH_RECORD *wrec, float norm_height); - FLOAT32 prioritize_state(CHUNKS_RECORD *chunks_record, - SEARCH_RECORD *the_search); - FLOAT32 width_priority(CHUNKS_RECORD *chunks_record, - STATE *state, - int num_joints); - FLOAT32 seamcut_priority(SEAMS seams, - STATE *state, - int num_joints); - FLOAT32 rating_priority(CHUNKS_RECORD *chunks_record, - STATE *state, - int num_joints); // Member variables. LanguageModel *language_model_; PRIORITY pass2_ok_split; - int pass2_seg_states; - int num_joints; - int num_pushed; - int num_popped; - BlobMatchTable blob_match_table; - EVALUATION_ARRAY last_segmentation; // Stores the best choice for the previous word in the paragraph. // This variable is modified by PAGE_RES_IT when iterating over // words to OCR on the page. @@ -508,9 +420,9 @@ class Wordrec : public Classify { GenericVector blame_reasons_; // Function used to fill char choice lattices. void (Wordrec::*fill_lattice_)(const MATRIX &ratings, - const LIST &best_choices, - const UNICHARSET &unicharset, - BlamerBundle *blamer_bundle); + const WERD_CHOICE_LIST &best_choices, + const UNICHARSET &unicharset, + BlamerBundle *blamer_bundle); protected: inline bool SegSearchDone(int num_futile_classifications) { @@ -544,38 +456,38 @@ class Wordrec : public Classify { // best_choice_bundle: a collection of variables that should be updated // if a new best choice is found // - void UpdateSegSearchNodes(int starting_col, - SEG_SEARCH_PENDING_LIST *pending[], - BestPathByColumn *best_path_by_column[], - CHUNKS_RECORD *chunks_record, - HEAP *pain_points, - BestChoiceBundle *best_choice_bundle, - BlamerBundle *blamer_bundle); + void UpdateSegSearchNodes( + float rating_cert_scale, + int starting_col, + GenericVector* pending, + WERD_RES *word_res, + LMPainPoints *pain_points, + BestChoiceBundle *best_choice_bundle, + BlamerBundle *blamer_bundle); // Process the given pain point: classify the corresponding blob, enqueue // new pain points to join the newly classified blob with its neighbors. void ProcessSegSearchPainPoint(float pain_point_priority, const MATRIX_COORD &pain_point, - const WERD_CHOICE *best_choice, - SEG_SEARCH_PENDING_LIST *pending[], - CHUNKS_RECORD *chunks_record, - HEAP *pain_points, + const char* pain_point_type, + GenericVector* pending, + WERD_RES *word_res, + LMPainPoints *pain_points, BlamerBundle *blamer_bundle); + // Resets enough of the results so that the Viterbi search is re-run. + // Needed when the n-gram model is enabled, as the multi-length comparison + // implementation will re-value existing paths to worse values. + void ResetNGramSearch(WERD_RES* word_res, + BestChoiceBundle* best_choice_bundle, + GenericVector* pending); // Add pain points for classifying blobs on the correct segmentation path // (so that we can evaluate correct segmentation path and discover the reason // for incorrect result). - void InitBlamerForSegSearch(const WERD_CHOICE *best_choice, - CHUNKS_RECORD *chunks_record, - HEAP *pain_points, + void InitBlamerForSegSearch(WERD_RES *word_res, + LMPainPoints *pain_points, BlamerBundle *blamer_bundle, STRING *blamer_debug); - - // Analyze the contents of BlamerBundle and set incorrect result reason. - void FinishBlamerForSegSearch(const WERD_CHOICE *best_choice, - BlamerBundle *blamer_bundle, - STRING *blamer_debug); - };